一起了解 Redis 中的 HyperLogLog 算法?
如果觉得对你有帮助,能否点个赞或关个注,以示鼓励笔者呢?!博客目录 | 先点这里
-
前提概念
- 需求
- 什么是 HyperLogLog ?
- 什么是基数?
- 基数统计
-
应用
- 需求
- 应用
-
原理
- 伯努利试验
- 原理
-
Redis 实践
- Redis HyperLogLog 与伯努利试验的联系?
- Redis HyperLogLog 的实现?
前提概念
什么是 HyperLogLog?
HyperLogLog
HyperLogLog 是一种算法,其来源于论文 《HyperLogLog the analysis of a near-optimal cardinality estimation algorithm》,是 LogLog 算法的升级版本,主要的目的就是利用统计学原理做近似基数统计。优势就是利用一个较小的固定大小空间计算任意大小的 Distinct Value。

Redis 的实践
HyperLogLog 并非 Redis 一家独有,Redis 只是基于 HyperLogLog 算法实现可一个 HyperLogLog 数据结构,并用该数据结构提供基数统计的功能。其优势就是可以做到只需要 12 kb 的空间大小,就可以实现接近 2^64 量级的基数统计。
HyperLogLog 数据结构并不会保存真实的元数据,所以其核心就是基数估算算法
在工程实践中,通常会用于 App 或页面的 UV 统计
什么是基数?
什么是集合?
在了解数学概念上的基数之前,我们先来了解下在数学上,是怎么描述 “集合” 的。
集合 (Set), 也成集,是集合论的主要研究对象。集合是指具有某种特定性质的具体或抽象的对象汇总而成的集体。其中构成集合的这些对象则称之为该集合的元素
集合特性
确定性
给定一个集合,任给一个元素,该元素只可能属于或不属于该集合。不存在模棱两可的情况互异性
一个集合中,任何两个元素都可以被认为是不相同的,即每一个元素只会在集合中出现一次,具有去重性质无序性
一个集合中,所有元素的地位都是相同的,元素之间是无序的
在本文中重点记住,集合中的元素是具互异性的
什么是基数?
基数 (cardinal number) 在数学上,是集合论中刻画任意集合大小的一个概念。通俗点讲,基数就是对集合元素的计数。
-
结合对集合的理解,大白话就是,基数就是指一个集合中不同元素的个数
-
元素集合可以是有限集合或无限集合,如果是有限集合,那么集合的基数就是一个特定的自然数;如果是无限集合,那么其基数就不是一个自然数了
基数?集合大小
我们知道基数就是对集合元素的计数,那说白了不就是集合元素的个数吗?为什么要引申出基数的概念,不直接使用集合大小?
这里直接贴上知乎 @lvony 的回答
- 基数就是严格意义上的集合元素多寡,为什么不直接用集合大小或者元素个数来描述,我觉得主要是所有集合的基数集合不是自然数集合的子集。换言之所有的自然数都是基数,但不是所有的基数都是自然数
- 如无穷集合的基数都不是自然数
基数统计
我们可以知道,基数可以简单理解成集合中不同元素的个数。那么什么是基数统计呢?在工程实践大数据领域,要精确的计算基数是十分困难的。为什么这么说呢?假设你要实现大数据集合的精确基数统计,通常我们会有两种思路
Set
我们可以采用集合的方式去统计元素基数,需要存储数据的元数据。假设有 10 亿个用户,每个用户占用 64 位的空间,那么总共就需要 1000000000 * 64 / 8 / 1024 / 1024 /1024 = 7.45 GB。似乎咋一看还能接受,但是这只是单场景的基数统计,如果存在多个场景?时间范围再放大,那么空间就会跟随基数的增长而线性增长,这肯定是不能接受的
BitMap
那么既然 Set 占用空间过大,那么 BitMap 如何?位图倒是一个可以考虑的选项,将数据经过 hash 得到位图上的索引,并将该索引标记为 1,最后经过统计位图中 1 的个数,就可以做到基数统计。但是呢,也是有缺点的,因为采用的是哈希算法,那么必然存在哈希冲突,所以位图统计也并非是一个严谨的准确基数统计。为了降低冲突,必然会将位图的大小大于集合基数的一定倍数。
假设仍然是 10 亿用户数,假设位图的大小是基数的 2 倍,那么需要的空间就是 1000000000 * 2 / 8 /1024/1024 = 238 MB, 这相比 Set 的空间占用,似乎已经可以接受了
概率算法
综上,位图的确是一个不错的选择,只需要 MB 级的空间就可以做 10 亿级别的基数统计。
但是如果有人跟你说它只需要 KB 级别的空间就可以做跟你同等量级甚至更大的基数统计呢?那位图还香吗? 在不追求绝对准确的情况下,倒是有很多基于概率算法的基数统计解决方案,比如
Linear CountingLogLog CountingHyerLogLog CountingHyperLogLog++
而 Redis 则是采用 HyperLogLog 这种算法去实现的
应用
需求
- 统计页面
UV- 统计 APP 或页面,每天被点击/浏览/观看的用户数量
- 注意是
UV, 非PV
- 不能占用大量空间,即非 O(n) 的空间复杂度
应用
Redis 原生提供了 HyperLogLog ,它是 LogLog 算法的升级版本,可以为我们提供 “非精准的去重计数估算”,常用于 APP 或页面的 UV 统计
- Redis Command

Redis 只为 HyperLogLog 提供了三个命令,所以还是相当简单的,我们一起来看一下 pfadd key element [element...]
O(1) 的插入时间复杂度, 向指定 key 的 HyperLogLog 数据结构插入一或多个元素pfcount key [key...]
查询某 key 在 HyperLogLog 数据结构中的近似基数,如果该 key 不存在,则为 0pfmerge destkey sourcekey [sourcekey...]
将多个 HyperLogLog 合并到一个 HyperLogLog
原理
伯努利试验
什么是伯努利试验?
伯努利试验 (Bernoulli trial)
伯努利试验 (Bernoulli Trial) 是在相同的条件下,重复地,互相独立地进行的一种随机试验
- 该随机试验只有两种互斥结果:成功 或 失败
- 设试验只有 2 个可能结果: A A A 与 A ‾ \overline{A} A ,则称实验为 E E E 伯努利试验;设

HyperLogLog是一种用于近似基数统计的算法,旨在使用有限空间处理大量唯一值。在Redis中,HyperLogLog数据结构实现了这一算法,用于高效估算UV等大数据集合的基数。其核心原理基于伯努利试验,通过记录每个桶内伯努利过程的最大k值,然后取所有桶的k值平均,用估算公式计算基数。Redis的每个HyperLogLog占用12kb空间,理论上可支持2^64量级的数据量。
最低0.47元/天 解锁文章
1047





