使用redis中的bitmap做个用户签到统计功能

711 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第5天,点击查看活动详情


bitmap,用0和1来表示状态的bit数组

在基础的数据类型中

1个bit,是一个0或1,就是二进制

1个byte,是8个bit

8bit = 1byte

bitmap就是使用bit数组来表示0或1,类似于下面这种

10100001

比如我们做一个签到功能,1个用户1年365天的签到记录,也只需用到365bit

365/8=45.6byte,可以极大的节省空间

基础命令
# 设置值 value只接受01
setbit key offset value
# 获取值
getbit key offset
# startend非必填,不写的话,查询的是key里面含有value=1的总共有多少个?
bitcount key [start] [end]

统计一周7天用户签到次数

第一天,用户签到了
setbit user:1001 0 1
第二天,用户没签到
setbit user:1001 1 0
第三天,用户签到了
setbit user:1001 2 1
第四天,用户签到了
setbit user:1001 3 1
第五天,用户签到了
setbit user:1001 4 1
第六天,用户没签到了
setbit user:1001 5 0
第七天,用户没签到了
setbit user:1001 6 0

可以看到, offset 偏移量是从0开始的,因为是数组嘛,下标从0开始

这个时候,如果我想看该用户第三天和第七天有没有签到

> getbit user:1001 2
1
> getbit user:1001 6
0

1代表签到了,0代表没签到

查询这7天,总共有几天签到了

> bitcount user:1001 0 6
4

这里 offset 偏移量,也可以写成日期,比如

# 26号没签到
setbit user:1001 20220626 0
# 27号签到了
setbit user:1001 20220627 1

bitmap 底层编码使用的也是 string 类型

> type user:1001
string

但其实质是二进制的ascii编码

比如我们执行一下下面几步操作

> setbit user:1005 1 1
0
> setbit user:1005 2 1
0
> setbit user:1005 7 1
0
> get user:1005
a

这个时候,bitmap的数组应该如下

01100001

二进制 01100001 对应的 10 进制是 97,对应的 16进制是61,对应的ascii编码是小写的a

image.png

在 redis 中的显示

image.png


使用bitmap统计连续签到用户数

这里需要对用户做一下映射:

user:1001 -> 1

user:1002 -> 2

user:1003 -> 3

25号都签到了
> setbit 20220625 1 1
0
> setbit 20220625 2 1
0
> setbit 20220625 3 1
0

26号,3号用户没签到
> setbit 20220626 1 1
0
> setbit 20220626 2 1
0
> setbit 20220626 3 0
0

27号,还是3号用户没签到
> setbit 20220627 1 1
0
> setbit 20220627 2 1
0
> setbit 20220627 3 0
0

统计 25、26、27三天都签到的用户有多少

先对 20220625、20220626、20220627 三个bitmap做与运算

然后再对计算结果(threeday)做bitcount统计

> bitop and threeday 20220625 20220626 20220627
1
> bitcount threeday
2

最终统计出来的数据就是这3天都签到的用户数量

bitop 与运算逻辑

用户1用户2用户3
20220625111
20220626110
20220627110
连续签到合计110

与运算规则: 有0就是0,全是1才是1