背景
记录百万用户的签到情况用什么数据结构?
假如我们在判断用户是否登陆的场景中使用 Redis 的 String 类型实现(key -> userId,value -> 0 表示下线,1 - 登陆),假如存储 100 万个用户的登陆状态,如果以字符串的形式存储,就需要存储 100 万个字符串了,内存开销太大。
String为什么开销大?
因为String不仅要保存原始数据,还需要保存len(buf 的已用长度)和alloc(buf 实际分配长度)
对于值的状态只有两种这种情况,即二值状态场景,建议使用Bitmap。比如1亿用户的登陆状态只需使用1亿个bit,大约12MB。
Bitmap 底层实现
应用场景
如何实现某个用户每个月的签到情况?比如统计UID 89757 的用户在 2021 年 5 月份的打卡情况要如何进行?
key 可以设计成 uid:sign:{userId}:{yyyyMM},offset 为当天 - 1,因为位图的offset(下标索引)从0开始
第一步,执行下面指令表示记录用户在 2021 年 5 月 16 号打卡。
SETBIT uid``:``sign``:89757:202105`` ``15`` ``1
第二步,判断编号 89757 用户在 2021 年 5 月 16 号是否打卡。
GETBIT uid``:``sign``:89757:202105`` ``15
第三步,统计该用户在 5 月份的打卡次数,使用 BITCOUNT 指令统计值 = 1 的 bit 位的数量。
BITCOUNT uid``:``sign``:89757:202105
如何统计这个月首次打卡时间呢?
使用 BITPOS key value [start] [end]指令,返回数据表示 Bitmap 中第一个值为 value 的 offset 位置。
我们可以通过执行以下指令来获取 userID = 89757 在 2021 年 5 月份首次打卡日期:
BITPOS uid``:``sign``:89757:202105 1
需要注意的是,我们需要将返回的 value + 1 ,因为 offset 从 0 开始。
在记录了一个亿的用户连续 7 天的打卡数据,如何统计出这连续 7 天连续打卡用户总数呢?
日期作为 key ,uid作为 offset,若是打卡则将 offset 位置的 bit 设置成 1。设置7个这样的key。
对这7个Bitmap做与运算,最终结果中对应位仍为1的就是连续打卡7天的,可以使用BITCOUNT统计满足连续打卡7天的用户数量。
指令:BITOP operation destkey key [key ...],opration 可以是 and、OR、NOT、XOR。