Bitmap的使用

67 阅读2分钟

背景

记录百万用户的签到情况用什么数据结构?

假如我们在判断用户是否登陆的场景中使用 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 可以是 andORNOTXOR