一个亿的keys,Redis如何统计?( 二 )


在签到统计中,0和1只占了一个bit,即使一年的签到数据才365个bit位 。大大减少了存储空间 。
Bitmap 提供了GETBIT/SETBIT 操作,使用一个偏移值 offset 对 bit 数组的某一个 bit 位进行读和写 。不过,需要注意的是,Bitmap 的偏移量是从 0 开始算的,也就是说 offset 的最小值是 0 。当使用 SETBIT 对一个 bit 位进行写操作时,这个 bit 位会被设置为 1 。Bitmap 还提供了 BITCOUNT 操作,用来统计这个 bit 数组中所有1的个数 。
键值如何设计呢?key可以是userid:yyyyMM,即是唯一id加上月份 。假设员工id为10001,需要统计2020/11月份的签到打卡记录 。
第一步,执行命令设置值,假设11月2号打卡了,命令如下:
SETBIT userid:10001:202011 1 1
BitMap是从下标0开始,因此2号则是下标为1,值设置为1则表示成功打卡了 。
第二步,检查该用户11月2号是否打卡了,命令如下:
GETBIT userid:10001:202011 1
第三步,统计11月的打卡次数,命令如下:
BITCOUNT userid:10001:202011
那么问题来了,需要统计你这个签到系统中连续20天的签到打卡的用户的总数,如何处理呢?假设用户一个亿 。
比如需要统计2020/11/01到2020/11/20天中连续打卡的人数,如何统计呢?
Bitmap中还支持同时对多个BitMap按位做与、或、异或操作,命令如下图:

一个亿的keys,Redis如何统计?

文章插图
思路来了,我们可以将每天的日期作为一个key,对应的BitMap存储一亿个用户当天的打卡情况 。如下图:
一个亿的keys,Redis如何统计?

文章插图
此时我们只需要对2020/11/1到2020/11/20号的Bitmap做按位与操作,最终得到的一个Bitmap中每个bit位置对应的值则代表连续20天打卡的情况,只有连续20天全部打卡,所在的bit位的值才为1 。如下图:
一个亿的keys,Redis如何统计?

文章插图
最终可以使用BITCOUNT命令进行统计 。
可以尝试计算下内存开销,每天使用 1 个 1 亿位的 Bitmap,大约占 12MB 的内存(10^8/8/1024/1024),20 天的 Bitmap 的内存开销约为 240MB,内存压力不算太大 。不过,在实际应用时,最好对 Bitmap 设置过期时间,让 Redis 自动删除不再需要的签到记录,以节省内存开销 。
如果涉及到二值状态,比如用户是否存在,签到打卡,商品是否存在等情况可以使用Bitmap,可以有效节省内存空间 。
四、基数统计
基数统计指统计一个集合中不重复元素的个数 。
举个例子:电商网站中通常需要统计每个网页的UV来确定权重,网页的UV肯定是需要去重的,在Redis类型中Set支持去重,第一时间肯定想到的是Set 。
但是这里有一个问题,Set底层使用的是哈希表和整数数组,如果一个网页的UV达到千万级别的话(一个电商网站中何止一个页面),那么对于内存的消耗极大 。
Redis提供了一个扩展类型HyperLogLog用于基数统计,计算2^64个元素大概只需要12KB的内存空间 。
是不是很心动?但是HyperLogLog是存在误差的,大概是在0.81%,如果需要精准的统计,还是需要使用Set 。对于这种网页的UV来说,足够了 。
在统计网页UV的时候,只需要将用户的唯一id存入HyperLogLog中,如下:
PFADD p1:uv 10001 10002 10003 10004
如果存在重复的元素,将会自动去重 。
统计也很简单,使用PFCOUNT命令,如下:
PFCOUNT p1:uv
总结
本文介绍了统计的几种类型以及应该用什么集合存储,为了方便理解,作者将支持情况和优缺点汇总了一张表格,如下图:
一个亿的keys,Redis如何统计?

文章插图
Set和Sorted Set支持交集、并集的聚合运算,但是Sorted Set不支差集运算 。
Bitmap也能对多个Bitmap做与、异或、或的聚合运算 。
List和SortedSet都支持排序统计,但是List是根据元素先后插入顺序排序,Sorted Set支持权重,相对于List排序来说更加灵活 。
对于二值状态统计,判断某个元素是否存在等场景,建议使用Bitmap,节省的内存空间 。
对于基数统计,在大数据量、不要求精准的情况建议使用HyperLogLog,节省内存空间;对于精准的基数统计,最好还是使用Set集合 。
作者丨不才陈某
来源丨公众号:码猿技术专栏(ID:oneswholife)




推荐阅读