1.10万用户一年365天的登录情况如何用redis存储,并快速检索任意时间窗内的活跃用户?
可以考虑使用BitMap,位图;
具体做法:使用时间作为cacheKey,然后用户ID为offset,如果当日活跃过就设置为1,那么我该如果计算某几天/月/年的活跃用户呢(暂且约定,统计时间内只有有一天在线就称为活跃),有请下一个redis的命令: BITOP operation destkey key [key ...]
说明:对一个或多个保存二进制位的字符串 key 进行位元操作,并将结果保存到 destkey 上。
说明:BITOP 命令支持 AND 、 OR 、 NOT 、 XOR 这四种操作中的任意一种参数
bitop and dest1 2021-08-01,2021-08-21
bitcount dest1
引申一下->用来做当前用户在线状态会不会更节省空间呢?
使用bitmap是一个节约空间效率又高的一种方法,只需要一个key,然后用户ID为offset,如果在线就设置为1,
不在线就设置为0,和上面的场景一样,5000W用户只需要6MB的空间。
来看一下 bitmap的效率:
场景1:1 亿用户,5千万独立
数据类型 | 每个 userid 占用空间 | 需要存储的用户量 | 全部内存量 |
---|---|---|---|
set | 32位(假设userid用的是整型,实际很多网站用的是长整型) | 50,000,000 | 32位 * 50,000,000 = 200 MB |
BitMap | 1 位 | 100,000,000 | 1 位 * 100,000,000 = 12.5 MB |
一天 | 一个月 | 一年 | |
---|---|---|---|
set | 200M | 6G | 72G |
BitMap | 12.5M | 375M | 4.5G |
场景2:只有 10 万独立用户
数据类型 | 每个 userid 占用空间 | 需要存储的用户量 | 全部内存量 |
---|---|---|---|
set | 32位(假设userid用的是整型,实际很多网站用的是长整型) | 1,000,000 | 32位 * 1,000,000 = 4 MB |
BitMap | 1 位 | 100,000,000 | 1 位 * 100,000,000 = 12.5 MB |
通过上面的对比,我们可以看到,如果独立用户数量很多,使用 BitMap 明显更有优势,能节省大量的内存。但如果独立用户数量较少,还是建议使用 set 存储,BitMap 会产生多余的存储开销。
- type = string,BitMap 是 sting 类型,最大 512 MB。
- 注意 setbit 时的偏移量,可能有较大耗时
- 位图不是绝对好。
2.100万并发4G数据,10万并发400G数据,如何设计Redis存储方式?
考虑使用Redis-cluster 做好数据分片处理.redis cluster集群的部署、在线扩容、管理、监控等又成了问题,需要有一个非常好用的自动化运维管理平台才可以胜任,不然得累死运维了。所以,要用好redis cluster,也还是有很多事情要做的。
3.基于Redis实现热搜榜.
Redis的有序集合ZSET来实现,搜索榜其实主要分为两步:
保存搜索词,将搜索词保存到redis的同时需要给他一个分值,如果是同一个搜索词分值加一,另外搜索榜一般都是当日的不像微博那么多人使用可以是每个小时的,所以我们要设计一个统一过期时间,这里我设置的是当日晚上十二点
获取搜索词,我们只需要获取搜索词的前五位获取前十位就可以了,同时我们也要获取他的分值也就是搜索次数.
[点赞榜,阅读榜,热度榜,也都是这么一个道理]
相同分数问题:
比如 分值相同,需要时间晚的显示在前面:可以把分值放到若干倍 必须10000000000000 后面0的部分用时间戳填充 最终根据最后结果排序
在分数中加入时间戳,计算公式为:
带时间戳的分数 = 实际分数*10000000000 + (9999999999 – timestamp)
timestamp我们采用系统提供的time()函数,也就是1970年1月1日以来的秒数,我们采用32位的时间戳(这能坚持到2038年),由于32位时间戳是10位十进制整数(最大值4294967295),所以我们让时间戳占据低10位(十进制整数),实际分数则扩大10^10倍,然后把两部分相加的结果作为zset的分数。考虑到要按时间倒序排列,所以时间戳这部分需要颠倒一下,这便是用9999999999减去时间戳的原因。当我们要读取玩家实际分数时,只需去掉后10位即可。
这里面有两个问题。
第一个问题是小问题,采用秒为时间戳可能区分度还不够,如果同一秒出现两个分数相同的仍然会出现前面的问题,当然我们可以选择精度更高的时间戳,但在实际场景中,同一秒谁排前面已经无关紧要。
第二个问题是大问题,因为Redis的分数类型采用的是double,64位双精度浮点数只有52位有效数字,它能精确表达的整数范围为-2^53到2^53,最高只能表示16位十进制整数(最大值为9007199254740992,其实连16位也不能完整表示)。这就是说,如果前面时间戳占了10位的话,分数就只剩下6位了,这对于某些排行榜分数来说是不够用的。我们可以考虑缩减时间戳位数,比如从2015年1月1日开始计时,但这仍然增加不了几位。或者减少区分度,以分钟、小时来作为时间戳单位。
4.基于Redis实现推荐系统.
请看之前的博文: 基于Redis实现推荐系统
5.基于Redis实现购物车.
使用Redis的hash结构.
购物车基本功能:
1.购物车列表 2.加入购物车 3.删除购物车某个商品 4.清空购物车
方案:
- 以客户id作为key,每位用户创建一个hash存储结构存储对应的购物车信息
- 将商品编号作为field,购买数量作为value进行存储
- 添加商品:追加全新的field和value
- 浏览:遍历hash
- 更改数量: 自增/自减,设置value值
- 删除商品: 删除field
- 清空: 删除key
当前仅仅是将数据存储到redis中,并没有起到加速的作用,商品信息还需要二次查询数据库
-
每条购物车中的商品记录保存成两条field
-
filed1专用于保存购买数量
命名格式: 商品id:nums
保存数据: 数值
-
field2专用于保存购物车中显示的信息,包含文字描述,图片地址,所属商家信息等
命名格式: 商品id:info
保存数据: json
当然这里也可以直接使用下面的方案:
- 以客户id作为key,每位用户创建一个hash存储结构对应的购物车信息.
- 将商品编号作废field,商品信息(数量,单价,总价等等)作为value进行同意存储.
暂时就碰到过这么多问题,如果哪位同学有新的问题,可以一起交流一下.