Redis HyperLogLog 基数统计和 Bitmap位图场景详解

1、HyperLogLog 基数统计

什么是基数?

比如数据集 {1, 3, 5, 7, 5, 7, 8}, 那么这个数据集的基数集为 {1, 3, 5 ,7, 8}, 基数(不重复元素)为5。 基数估计就是在误差可接受的范围内,快速计算基数。

Redis HyperLogLog

Redis 在 2.8.9 版本添加了 HyperLogLog 结构。

Redis HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定 的、并且是很小的。

在 Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基 数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。

但是,因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以 HyperLogLog 不能像集合那样,返回输入的各个元素。

常用命令:

序号 命令及描述
1 [PFADD key element [element …]添加指定元素到 HyperLogLog 中。
2 [PFCOUNT key [key …] 返回给定 HyperLogLog 的基数估算值。
3 [PFMERGE destkey sourcekey [sourcekey …] 将多个 HyperLogLog 合并为一个 HyperLogLog

使用实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#添加指定元素到 HyperLogLog 中
127.0.0.1:6379> pfadd key1 a b c d e f g h
(integer) 1
#返回给定 HyperLogLog 的基数估算值
127.0.0.1:6379> pfcount key1
(integer) 8
127.0.0.1:6379> pfadd key2 e f g h x w y x z
(integer) 1
127.0.0.1:6379> pfcount key2
(integer) 8
#将多个 HyperLogLog 合并为一个 HyperLogLog
127.0.0.1:6379> pfmerge key3 key1 key2
OK
127.0.0.1:6379> pfcount key3
(integer) 12
123456789101112131415

2、Bitmap位图场景详解

bitmap就是通过最小的单位bit来进行0或者1的设置,表示某个元素对应的值或者状态。一个bit的值,或者是0,或者是1;也就是说一个bit能存储的最多信息是2。

Redis从2.2.0版本开始新增了setbit,getbit,bitcount等几个bitmap相关命令。虽然是新命令,但是并没有新增新的数据类型,因为setbit等命令只不过是在set上的扩展。

语法详解:

1
2
setbit key offset value
1

对key所存储的字符串值,设置或清除指定偏移量上的位(bit),如果不存在,就设置;如果已存在,就清除原有的,设置新的。

返回值为该位在setbit之前的值,value只能取0或1,offset(偏移量)从0开始,即使原位图只能10位,offset可以取1000

1
2
getbit key offset
1

对key所存储的字符串值,获取指定偏移量上的位(bit)

1
2
bitcount key [start end]
1

获取位图指定范围中位值为1的个数,如果不指定start与end,则取所有

使用场景1:统计一周打卡次数

0-6为周一到周末,1表示已打卡,0表示未打卡

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#========================记录一周内的打卡情况========================
127.0.0.1:6379> setbit sign 0 1
(integer) 0
127.0.0.1:6379> setbit sign 1 1
(integer) 0
127.0.0.1:6379> setbit sign 2 0
(integer) 0
127.0.0.1:6379> setbit sign 3 0
(integer) 0
127.0.0.1:6379> setbit sign 4 1
(integer) 0
127.0.0.1:6379> setbit sign 5 1
(integer) 0
127.0.0.1:6379> setbit sign 6 0
(integer) 0
#========================查看具体某一天的打卡情况========================
127.0.0.1:6379> getbit sign 2
(integer) 0
#========================统计一周内打卡次数========================
127.0.0.1:6379> bitcount sign
(integer) 4

12345678910111213141516171819202122

使用场景2:统计活跃用户

使用时间作为Key,然后用户ID为offset,如果当日活跃过就设置为1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#记录2021年08月12日用户的状态
127.0.0.1:6379> setbit 20210812 1 1
(integer) 0
127.0.0.1:6379> setbit 20210812 2 0
(integer) 0
127.0.0.1:6379> setbit 20210812 3 0
(integer) 0
#查看指定用户在2021年08月12日是否活跃
127.0.0.1:6379> getbit 20210812 2
(integer) 0
#查看在2021年08月12日活跃的用户数
127.0.0.1:6379> bitcount 20210812
(integer) 1

1234567891011121314

使用bitmap是一个节约空间效率又高的一种方法