在大规模数据处理的场景中,我们经常需要统计某个字段的去重数量,比如统计某段时间内独立访客数、独立商品点击数等。当数据量达到亿级甚至更高时,传统的COUNT DISTINCT操作会因为需要存储所有去重值而占用大量内存,查询耗时也会急剧上升。HyperLogLog算法可以在仅用几KB内存的情况下,完成对海量数据的近似去重计数,误差通常可以控制在1%以内,非常适合大规模数据下的近似计数需求。

HyperLogLog算法核心原理
HyperLogLog算法的核心思想是基于伯努利过程,通过统计哈希值中第一个1出现的位置来估算基数。简单来说,算法会对每个输入元素计算哈希值,记录哈希值二进制表示中第一个1出现的位置的最大值,再通过统计公式估算出总的去重元素数量。
算法的主要优势在于内存占用极低,对于误差率0.81%的配置,仅需要12KB的内存就可以统计接近2^64个不同元素,非常适合大规模数据的计数场景。
常见数据库中的HyperLogLog支持
目前很多主流数据库都原生支持HyperLogLog相关的函数,不需要开发者自己实现算法逻辑,常见的支持情况如下:
| 数据库类型 | 相关函数 | 说明 |
|---|---|---|
| Redis | PFADD、PFCOUNT、PFMERGE | 原生支持HyperLogLog结构,操作简单高效 |
| PostgreSQL | hll_add_agg、hll_cardinality | 需要通过hll扩展启用,支持聚合计算 |
| ClickHouse | uniqCombined | 底层基于HyperLogLog实现,直接用于近似去重计数 |
| MySQL | 无原生支持 | 需要通过UDF或者应用层实现 |
SQL中使用HyperLogLog实现近似计数示例
PostgreSQL中使用HyperLogLog
PostgreSQL需要先安装hll扩展,安装完成后可以使用相关函数进行近似计数:
-- 启用hll扩展
CREATE EXTENSION hll;
-- 创建测试表,存储用户访问记录
CREATE TABLE user_visit (
visit_id INT,
user_id INT,
visit_time TIMESTAMP
);
-- 插入测试数据
INSERT INTO user_visit VALUES
(1, 1001, '2024-01-01 10:00:00'),
(2, 1002, '2024-01-01 10:01:00'),
(3, 1001, '2024-01-01 10:02:00'),
(4, 1003, '2024-01-01 10:03:00'),
(5, 1002, '2024-01-01 10:04:00');
-- 使用HyperLogLog统计独立用户数
SELECT hll_cardinality(hll_add_agg(hll_hash_integer(user_id))) AS approximate_unique_users
FROM user_visit;
上述查询中,hll_hash_integer用于将user_id转换为哈希值,hll_add_agg用于聚合生成HyperLogLog结构,hll_cardinality用于计算近似基数,最终结果会返回近似的独立用户数,对于上述测试数据,结果应该接近3。
ClickHouse中使用HyperLogLog
ClickHouse的uniqCombined函数底层基于HyperLogLog实现,使用起来更加简单:
-- 创建测试表
CREATE TABLE user_visit (
visit_id UInt32,
user_id UInt32,
visit_time DateTime
) ENGINE = MergeTree()
ORDER BY visit_time;
-- 插入测试数据
INSERT INTO user_visit VALUES
(1, 1001, '2024-01-01 10:00:00'),
(2, 1002, '2024-01-01 10:01:00'),
(3, 1001, '2024-01-01 10:02:00'),
(4, 1003, '2024-01-01 10:03:00'),
(5, 1002, '2024-01-01 10:04:00');
-- 统计独立用户数
SELECT uniqCombined(user_id) AS approximate_unique_users
FROM user_visit;
MySQL中通过UDF实现近似计数
MySQL没有原生支持HyperLogLog,我们可以通过用户自定义函数结合应用层逻辑实现,也可以借助开源的MySQL HLL UDF插件,以下是简单的实现思路示例:
-- 假设已经安装了HLL UDF插件,相关函数名为hll_init、hll_add、hll_count
-- 创建测试表
CREATE TABLE user_visit (
visit_id INT,
user_id INT,
visit_time DATETIME
);
-- 插入测试数据
INSERT INTO user_visit VALUES
(1, 1001, '2024-01-01 10:00:00'),
(2, 1002, '2024-01-01 10:01:00'),
(3, 1001, '2024-01-01 10:02:00'),
(4, 1003, '2024-01-01 10:03:00'),
(5, 1002, '2024-01-01 10:04:00');
-- 统计独立用户数
SELECT hll_count(hll_add(hll_init(), user_id)) AS approximate_unique_users
FROM user_visit;
HyperLogLog的误差与适用场景
HyperLogLog的误差率和分配的内存大小有关,通常标准实现的误差率在1.3%左右,部分优化实现的误差率可以降到0.81%。它的适用场景主要是对计数精度要求不是100%准确,但是对性能、内存占用要求较高的场景,比如:
- 统计网站、APP的独立访客数
- 统计海量日志中的独立IP数
- 统计电商场景下的独立商品点击用户数
如果业务需要精确的去重计数,那么还是需要使用传统的COUNT DISTINCT操作,不过在数据量特别大的时候,可以考虑先对数据进行分片计数,再合并结果,或者采用其他精确计数优化方案。
总结
在大规模数据场景下,HyperLogLog算法为SQL中的近似计数提供了高效的解决方案,能够以极低的内存开销快速得到近似的去重计数结果。不同数据库对该算法的支持程度不同,开发者可以根据自己使用的数据库类型选择合适的实现方式。在实际使用中,需要结合业务的精度要求,合理选择精确计数或者近似计数方案,平衡性能和结果的准确性。
SQLHyperLogLog近似计数大规模数据修改时间:2026-06-12 06:51:20