HyperLogLog
HyperLogLog 不是一种新的数据结构 , 本质上是字符串类型。 是一种基数算法。 通过 HyperLogLog 可以节省内存空间,并完成独立总数的统计。
HyperLogLog 数据结构可用于仅使用少量恒定内存来计算集合中的唯一元素,具体而言,每个 HyperLogLog 为 12k 字节(加上键本身的几个字节)。
HyperLogLog 相关命令:
- PFADD: 将所有元素参数添加到存储在作为第一个参数指定的变量名中的HyperLogLog数据结构中。
- PFCOUNT: 使用单个键调用时,返回存储在指定变量处的HyperLogLog数据结构计算的近似基数,如果该变量不存在,则返回0。
- PFDEBUG:是一个内部命令。它是用来开发和测试Redis的。
- PFMERGE: 将多个HyperLogLog值合并为一个唯一的值,该值将近似于源HyperLogLog结构的观察集的并集的基数。
- PFSELFTEST: 是一个内部命令。它是用来开发和测试Redis的。
PFADD
添加元素,复杂度 O(1), 用于向 HyperLogLog 添加 元素 , 如果成功返回 1 :
PFADD key [element [element ...]]
向 键 2023_12-11:user 添加 元素
192.168.88.11:6380> PFADD 2023_12-11:user "user1" "user2" "user3" "user4" "user5" "user6"
(integer) 1
192.168.88.11:6380> PFADD 2023_12-12:user "user1" "user3" "user5" "user7" "user8" "user9"
(integer) 1
HyperLogLog 不是一种新的数据结构 , 本质上是字符串类型。 HyperLogLog 是一个 Redis 字符串,可以使用 GET 检索 和 SET 恢复。
192.168.88.11:6380> type 2023_12-11:user
string
192.168.88.11:6380> get 2023_12-11:user
"HYLL\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80WR\x80F\x19\x80E\xed\x8cF\x10\x84N\x92\x80@\xfc\x80F\xfd"
192.168.88.11:6380> type 2023_12-12:user
string
192.168.88.11:6380> get 2023_12-12:user
"HYLL\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80WR\x80F\x19\x80[\x91\x80B\xb9\x8cA\x7f\x84Ab\x88A]"
PFCOUNT
用于计算一个或多个HyperLogLog的独立总数, 当使用单个键调用时,复杂度 O(1),平均时间非常小。当调用多个键时,复杂度O(N),其中N是键的个数,时间要大得多。
如: 计算 2023_12-11:user 、 2023_12-12:user 的独立总数
192.168.88.11:6380> PFCOUNT 2023_12-11:user
(integer) 6
192.168.88.11:6380> PFCOUNT 2023_12-12:user
(integer) 6
往 2023_12-11:user 、 2023_12-12:user 插入新的元素:
192.168.88.11:6380> PFADD 2023_12-11:user "user10" "user11"
(integer) 1
192.168.88.11:6380> PFADD 2023_12-12:user "user15" "user16" "user20" "user21"
(integer) 1
再次 计算 2023_12-11:user 、 2023_12-12:user 的独立总数
192.168.88.11:6380> PFCOUNT 2023_12-11:user
(integer) 8
192.168.88.11:6380> PFCOUNT 2023_12-12:user
(integer) 10
计算 两个键 2023_12-11:user 、 2023_12-12:user 的独立总数 (3个重复用户: user1、user3、user5)
当使用多个键调用时,通过在内部将存储在提供键处的HyperLogLog合并为临时HyperLogLog,返回传递的HyperLogLog联合的近似基数。
返回的观察集基数并不精确,而是近似值,标准误差为 0.81%。
当PFCOUNT使用多个键调用时,会执行 HyperLogLogs 的即时合并,这很慢,而且联合的基数无法缓存,因此当使用多个键时PFCOUNT可能需要大约时间毫秒级,不应滥用。
192.168.88.11:6380> PFCOUNT 2023_12-11:user 2023_12-12:user
(integer) 15
PFMERGE
pfmerge 可以合并多个 HyperLogLog的并集赋值给 destkey
将多个 HyperLogLog 值合并为一个唯一值,该值将近似观察到的源 HyperLogLog 结构集的并集基数。
计算出的合并 HyperLogLog 设置为目标变量,如果不存在则创建该变量(默认为空 HyperLogLog)。
如合并 两个键 2023_12-11:user 、 2023_12-12:user 的独立总数:
192.168.88.11:6380> PFMERGE 2023_12-11-12:user 2023_12-11:user 2023_12-12:user
OK
192.168.88.11:6380> PFCOUNT 2023_12-11-12:user
(integer) 15
Memory
分别向 HyperLogLog、 Set 插入10万个 用户,并对比内存使用量,如下:
- 插入 10 万用户到 HyperLogLog:2023_12:users
192.168.88.11:6380> eval "for i=1,100000\ndo\n redis.call('pfadd', 'HyperLogLog:2023_12:users', 'user'..i)\nend\n" 0
(nil)
(0.63s)
# 返回的观察集基数并不精确,而是近似值, 即 : 99725/100000=99.7%, 误差: 0.27%
192.168.88.11:6380> PFCOUNT HyperLogLog:2023_12:users
(integer) 99725
# 内存使用量 12KB
192.168.88.11:6380> MEMORY USAGE HyperLogLog:2023_12:users
(integer) 12377
- 同样插入 10 万用户到 Set:2023_12:users
192.168.88.11:6380> eval "for i=1,100000\ndo\n redis.call('sadd', 'Set:2023_12:users', 'user'..i)\nend\n" 0
(nil)
(0.74s)
# 数量精确
192.168.88.11:6380> SCARD Set:2023_12:users
(integer) 100000
# 内存使用量 4.8MB
192.168.88.11:6380> MEMORY USAGE Set:2023_12:users
(integer) 5033019
小结
- HyperLogLog 数据结构可用于仅使用少量恒定内存来计算集合中的唯一元素, 内存占用非常小。 但是存在错误率。
- 可以容忍一定的错误率,返回的观察集基数并不精确,而是近似值,标准误差为 0.81%。
- 只是想统计独立总数、而不需要获取具体的单条数据。