Redis中的Bitmap和HyperLogLog
Bitmap(位图)
Bitmap存储结构
在Redis中以位为单位存储字符串,这种存储结构称为Bitmap,也叫位图。
每个字节存储结构如下:
offset | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|---|
value | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
setbit key 0 1 # 10000000
getbit key1 0 # 1
get key1 # "\x80"
setbit key2 1 1 # 01000000
setbit key2 7 1 # 01000001
get key2 # "A"
get key时,如果找到ASCII对应的字符串直接返回字符串,否则16进制数字。
示例如下:
Bitmap上的统计
bitcount count [start end]
setbit key 0 1 # 10000000
setbit key 8 1 # 10000000 10000000
setbit key 16 1 # 10000000 10000000 10000000
bitcount key # 3
bitcount key -2 -1 # 2
bitcount key -3 -1 # 3
bitcount是根据字节统计每位数值为1的位数,而不是以位统计;
end参数-1表示最后一个字节;
start参数是根据end参数往前推导。
bitop operation destkey key [key …]
operation可以是and、or、not、xor这四种操作中的任意一种。
- bitop and destkey key [key …] ,对一个或多个key求逻辑并,并将结果保存到destkey;
- bitop or destkey key [key …] ,对一个或多个key求逻辑或,并将结果保存到destkey;
- bitop xor destkey key [key …] ,对一个或多个key求逻辑异或,并将结果保存到destkey;
- bitop not destkey key ,对给定key求逻辑非,并将结果保存到destkey。
除了not操作外,其他操作都可以接受一个或多个key作为输入;
当bitop处理不同长度的字符串时,较短的那个字符串所缺少的部分会被看做0;
空的key也被看作是包含0的字符串序列。
setbit 20200723 0 1 # 10000000
setbit 20200724 0 1 # 10000000
setbit 20200724 7 1 # 10000001
bitop and key 20200723 20200724 # 10000000
bitcount key # 1
bitop or key 20200723 20200724 # 10000001
bitcount key # 2
Bitmap的妙用
用户在线状态
使用bitmap是一个节约空间效率又高的一种方法,只需要一个key,然后用户ID为offset,如果在线就设置为1,不在线就设置为0,和上面的场景一样,5000W用户只需要6MB的空间。
用户签到
设置一个日期作为开始日期,当用户签到时,使用用户id为key,当前日期到开始日期的天数为offset;
- 用户A第1天签到,用户id为1,setbit 1 1 1
- 用户B第2天签到,用户id为2,setbit 2 2 1
- 查询用户A第一天是否签到,getbit 1 1
- 计算用户A签到天数,bitcount 1
这里如果要根据日期范围来统计时就不能满足了,因为bitcount不是以位为单位来统计的,而是字节为单位统计;
如果要使用bitmap实现此需求,天数做offset时必须乘8,保证一天一个字节,统计时可直接使用天数计算start和end参数;
这种做法弊端就是使用了8倍的空间。
统计活跃用户
使用日期作为key,用户id作为偏移量,如果当天活跃,则设置value为1;
- 2020-07-23用户A活跃,用户id为1,setbit 20200723 1 1
- 2020-07-23用户B活跃,用户id为2,setbit 20200723 2 1
- 2020-07-24用户B活跃,用户id为2,setbit 20200724 2 1
- 假设日期范围内连续在线称为活跃用户,统计活跃用户数 bitop and key 20200723 20200724,bitcount key
HyperLogLog
HyperLogLog的指令
HyperLogLog提供了pfadd、pfcount和pfmerge指令,只占用12KB的空间
pfadd:将value加入集合中
pfcount:获取value计数值
pfadd key1 user1 #user1
pfadd key1 user2 #user1 user2
pfadd key1 user3 #user1 user2 user3
pfcount key1 #3
pfadd key2 user3 #user3
pfadd key2 user4 #user3 user4
pfmerge key key1 key2 #user1 user23 user3 user4
pfcount key #4
HyperLogLog使用场景
统计页面UV
以页面名称为key,访问页面的用户id为value,每次访问pfadd一次,统计时直接使用pfcount
不同页面的UV需要合并时,直接pfmerge,但是数据量大的时候,会有一定的误差
HyperLogLog原理
给定一系列随机整数样本,记录下低位连续零位的最大长度K,通过这个K值可以估算出样本数量N,即K和N具有线性相关性,不加权计算时,吻合下面公式曲线
N
=
2
K
N=2^K
N=2K
Redis中使用16384个桶,加权计算得出N值