TDengine 聚合函数 HYPERLOGLOG 用户手册

HYPERLOGLOG 函数用户手册(智能电表场景)

1. 函数概述

HYPERLOGLOG 是 TDengine 的聚合函数,用于估算数据集中不重复(去重)元素的数量。该函数基于 HyperLogLog 算法实现,是一种概率性基数估计算法,能够在使用很少内存的情况下,对超大数据集的基数进行估算。

重要说明:

  • TDengine 不支持 COUNT(DISTINCT column) 语法
  • TDengine 不支持在 HYPERLOGLOG 函数内使用 DISTINCT 关键字
  • HYPERLOGLOG 函数本身就具备去重统计功能

2. 语法

sql 复制代码
SELECT HYPERLOGLOG(column_name) FROM meters [WHERE condition] [GROUP BY clause];

参数说明

  • column_name:需要进行去重计数的字段,支持数值类型、字符串类型和时间戳类型
  • 只接受一个参数,不支持多参数
  • 不支持 DISTINCTHYPERLOGLOG(DISTINCT column_name) 是错误语法

返回值说明

  • 返回类型:BIGINT
  • 返回值意义:估算的不重复元素数量
  • 空值处理
    • 当输入全为 NULL 时,返回 0
    • NULL 值不参与计数,但不影响其他值的计数

3. 算法原理与实际意义

3.1 HyperLogLog 算法原理

HyperLogLog 是一种基于概率的基数估计算法,其核心思想是利用哈希函数的均匀分布特性:

基本原理
  1. 哈希映射:将输入值通过哈希函数映射为固定长度的二进制串
  2. 前导零统计:统计哈希值的前导零个数,前导零越多说明基数越大
  3. 分桶策略:将哈希空间划分为 m 个桶,每个桶记录观察到的最大前导零数
  4. 调和平均:使用调和平均数公式估算基数
数学基础
  • 如果一个数据集的基数为 n,那么其哈希值中约有 n/2 的值第一位为0,n/4 的值前两位为00,以此类推
  • 通过观察前导零的最大长度,可以估算出数据集的规模
  • 使用多个桶降低随机误差,提高估算精度

3.2 算法特点

  • 内存效率:使用固定大小内存(约1.5KB),不随数据量增长
  • 时间复杂度:O(1) 插入和查询时间
  • 精度控制:标准误差约为 1.04/√m,其中m为桶数量(通常为1024或2048)
  • 可合并性:支持分布式计算结果合并
  • 流式处理:支持增量数据处理

3.3 精度分析

根据 HyperLogLog 算法特性:

  • 理论误差:标准误差约为 1.04/√m ≈ 1.6%(m=1024时)
  • 实际误差:在大多数情况下误差在 2% 以内
  • 误差范围:误差随数据量增大而相对减小

3.4 实际应用意义

设备数量统计示例

假设我们要统计一周内有数据上报的电表数量:

sql 复制代码
-- 传统嵌套查询方法(内存消耗大,查询慢)
SELECT COUNT(*) FROM (
    SELECT DISTINCT device_id FROM meters WHERE ts >= NOW() - 7d
);

-- HYPERLOGLOG 方法(内存消耗小,查询快)
SELECT HYPERLOGLOG(device_id) FROM meters WHERE ts >= NOW() - 7d;

结果对比演示:

  • 实际设备数:10,000 台
  • 嵌套查询结果:10,000 台(精确)
  • HYPERLOGLOG 估算:9,987 台(误差 0.13%)
  • 内存使用对比:嵌套查询可能需要几十MB,HYPERLOGLOG 仅需 1.5KB
  • 查询时间对比:嵌套查询 5000ms,HYPERLOGLOG 50ms
业务价值分析
  1. 实时监控:快速了解设备活跃度,无需精确到个位数
  2. 容量规划:为系统扩容提供参考数据
  3. 趋势分析:识别设备数量变化趋势
  4. 异常检测:快速评估异常影响范围
算法选择策略
sql 复制代码
-- 小数据集(< 10万条):使用嵌套查询获得精确结果
SELECT COUNT(*) FROM (
    SELECT DISTINCT device_id FROM meters WHERE ts >= NOW() - 1h
);

-- 大数据集(> 100万条):使用 HYPERLOGLOG 获得快速估算
SELECT HYPERLOGLOG(device_id) FROM meters WHERE ts >= NOW() - 30d;

-- 中等数据集:根据精度要求选择
-- 需要精确结果时使用嵌套查询
-- 可接受近似结果时使用 HYPERLOGLOG

4. 与 SELECT DISTINCT 的对比分析

4.1 功能对比

特性 SELECT DISTINCT HYPERLOGLOG
功能 返回不重复的具体值 返回不重复值的数量估算
返回类型 原字段数据类型 BIGINT
内存使用 O(n) 线性增长 O(1) 固定约1.5KB
计算精度 100% 精确 约98-99% 精确
性能 大数据集时较慢 始终高速
结果用途 获取具体的不重复值 统计分析、监控指标

4.2 语法对比示例

sql 复制代码
-- SELECT DISTINCT:返回不重复的具体电压值
SELECT DISTINCT voltage FROM meters WHERE ts >= NOW() - 1h;
-- 返回结果:219.8, 220.1, 220.5, 221.2, ... (具体的电压值列表)

-- HYPERLOGLOG:返回不重复电压值的数量
SELECT HYPERLOGLOG(voltage) FROM meters WHERE ts >= NOW() - 1h;
-- 返回结果:2847 (表示有约2847个不同的电压值)

4.3 应用场景对比

SELECT DISTINCT 适用场景
sql 复制代码
-- 获取所有活跃的设备ID列表
SELECT DISTINCT device_id FROM meters WHERE ts >= NOW() - 24h;
-- 返回:['meter001', 'meter002', 'meter003', ...]

-- 获取所有上报数据的区域列表
SELECT DISTINCT location FROM meters WHERE ts >= NOW() - 7d;
-- 返回:['California.SanFrancisco', 'California.LosAngles', 'NewYork.NewYork', ...]

-- 获取所有异常电压值
SELECT DISTINCT voltage FROM meters 
WHERE (voltage < 210 OR voltage > 240) AND ts >= NOW() - 24h
ORDER BY voltage;
-- 返回:[195.2, 198.7, 205.1, 241.3, 245.8, ...] (具体异常电压值)
HYPERLOGLOG 适用场景
sql 复制代码
-- 统计活跃设备数量(用于监控大屏)
SELECT HYPERLOGLOG(device_id) FROM meters WHERE ts >= NOW() - 24h;
-- 返回:8650 (约8650台设备活跃)

-- 统计涉及区域数量
SELECT HYPERLOGLOG(location) FROM meters WHERE ts >= NOW() - 7d;
-- 返回:45 (约45个区域有数据)

-- 统计异常电压种类数量
SELECT HYPERLOGLOG(voltage) FROM meters 
WHERE (voltage < 210 OR voltage > 240) AND ts >= NOW() - 24h;
-- 返回:127 (约127种不同的异常电压值)

4.4 共同点与区别

共同点
  1. 去重功能:都能识别和处理重复值
  2. NULL处理:都会忽略NULL值
  3. 数据类型支持:都支持数值、字符串、时间戳类型
  4. WHERE条件:都可以配合WHERE条件过滤数据
主要区别

1. 返回内容不同

sql 复制代码
-- DISTINCT 返回具体值
SELECT DISTINCT groupid FROM meters;
-- 结果:1, 2, 3, 4, 5 (具体的组ID)

-- HYPERLOGLOG 返回数量
SELECT HYPERLOGLOG(groupid) FROM meters;
-- 结果:5 (有5个不同的组)

2. 内存消耗差异

sql 复制代码
-- DISTINCT 在大数据集上内存消耗巨大
SELECT DISTINCT device_id FROM meters WHERE ts >= NOW() - 30d;
-- 可能需要几百MB内存存储所有设备ID

-- HYPERLOGLOG 固定内存消耗
SELECT HYPERLOGLOG(device_id) FROM meters WHERE ts >= NOW() - 30d;
-- 只需要约1.5KB内存

3. 分组聚合的使用

sql 复制代码
-- DISTINCT 通常不与聚合函数直接组合(TDengine不支持COUNT(DISTINCT))
-- 需要使用嵌套查询实现计数
SELECT COUNT(*) FROM (
    SELECT DISTINCT device_id FROM meters WHERE ts >= NOW() - 24h
);

-- HYPERLOGLOG 直接支持分组聚合
SELECT location, HYPERLOGLOG(device_id) FROM meters 
WHERE ts >= NOW() - 24h 
GROUP BY location;

5. TDengine 中的去重计数实现方式

5.1 错误语法(TDengine不支持)

sql 复制代码
-- ❌ 错误:TDengine 不支持 COUNT(DISTINCT)
SELECT COUNT(DISTINCT device_id) FROM meters;

-- ❌ 错误:HYPERLOGLOG 不支持 DISTINCT
SELECT HYPERLOGLOG(DISTINCT device_id) FROM meters;

5.2 正确的替代方案

方案1:使用 HYPERLOGLOG(推荐)
sql 复制代码
-- ✅ 推荐:直接使用 HYPERLOGLOG
SELECT HYPERLOGLOG(device_id) FROM meters WHERE ts >= NOW() - 24h;
方案2:使用嵌套查询(精确计数)
sql 复制代码
-- ✅ 精确计数:嵌套查询实现
SELECT COUNT(*) FROM (
    SELECT DISTINCT device_id FROM meters WHERE ts >= NOW() - 24h
);
方案3:分组统计
sql 复制代码
-- ✅ 按区域统计设备数量
SELECT 
    location,
    HYPERLOGLOG(device_id) AS device_count
FROM meters 
WHERE ts >= NOW() - 7d
GROUP BY location;

-- ✅ 嵌套查询方式(精确)
SELECT 
    location,
    device_count
FROM (
    SELECT 
        location,
        COUNT(*) AS device_count
    FROM (
        SELECT DISTINCT location, device_id FROM meters WHERE ts >= NOW() - 7d
    )
    GROUP BY location
);

6. 返回值实例解析

6.1 基本计数示例

sql 复制代码
-- 示例数据:device_id 列包含 [1, 2, 2, 3, 3, 3, 4, 5, 5]
SELECT HYPERLOGLOG(device_id) FROM meters;
-- 返回值:5 (表示有5个不重复的设备ID:1,2,3,4,5)

-- 对比 SELECT DISTINCT
SELECT DISTINCT device_id FROM meters;
-- 返回值:1, 2, 3, 4, 5 (具体的设备ID列表)

6.2 NULL值处理示例

sql 复制代码
-- 示例数据:voltage 列包含 [220.1, 219.8, NULL, 220.1, 221.0, NULL, 219.8]
SELECT HYPERLOGLOG(voltage) FROM meters;
-- 返回值:3 (忽略NULL,不重复的电压值:220.1, 219.8, 221.0)

-- 对比 SELECT DISTINCT  
SELECT DISTINCT voltage FROM meters WHERE voltage IS NOT NULL;
-- 返回值:219.8, 220.1, 221.0 (具体的电压值)

6.3 分组统计示例

sql 复制代码
-- 按区域分组统计每个区域的设备数量
SELECT location, HYPERLOGLOG(device_id) FROM meters GROUP BY location;

返回结果:

复制代码
location                | hyperloglog(device_id)
California.SanFrancisco | 1250
California.LosAngles    | 980  
NewYork.NewYork        | 1150

对比 DISTINCT 方式:

sql 复制代码
-- 获取每个区域的具体设备列表(嵌套查询实现计数)
SELECT 
    location,
    COUNT(*) AS device_count
FROM (
    SELECT DISTINCT location, device_id FROM meters
) 
GROUP BY location;

7. 智能电表应用场景

基于智能电表数据库结构:

sql 复制代码
CREATE TABLE meters (
    ts TIMESTAMP,
    voltage FLOAT,
    current FLOAT,
    power FLOAT,
    device_id VARCHAR(50),
    region VARCHAR(100)
) TAGS (
    groupid INT,
    location VARCHAR(50)
);

7.1 设备统计分析

活跃设备数量统计
sql 复制代码
-- 统计过去24小时内有数据上报的设备数量
SELECT HYPERLOGLOG(device_id) AS active_devices FROM meters 
WHERE ts >= NOW() - 24h;
-- 返回例如:8650 (约8650台设备活跃)

-- 如需具体设备列表,使用 DISTINCT
SELECT DISTINCT device_id FROM meters WHERE ts >= NOW() - 24h;
-- 返回具体设备ID列表

-- 按小时统计活跃设备数量趋势
SELECT 
    _wstart AS hour_time,
    HYPERLOGLOG(device_id) AS active_devices 
FROM meters 
WHERE ts >= NOW() - 24h
INTERVAL(1h);

7.2 异常事件分析

异常设备统计
sql 复制代码
-- 统计电压异常的设备数量(快速评估影响范围)
SELECT HYPERLOGLOG(device_id) AS abnormal_devices FROM meters 
WHERE (voltage < 210 OR voltage > 240) AND ts >= NOW() - 24h;
-- 返回例如:125 (约125台设备异常)

-- 如需异常设备具体列表(用于维修派单)
SELECT DISTINCT device_id FROM meters 
WHERE (voltage < 210 OR voltage > 240) AND ts >= NOW() - 24h;
-- 返回具体异常设备ID列表

-- 统计异常电压值的种类数量
SELECT HYPERLOGLOG(voltage) AS voltage_types FROM meters 
WHERE (voltage < 210 OR voltage > 240) AND ts >= NOW() - 24h;
-- 返回例如:87 (约87种不同异常电压值)

7.3 性能对比实例

sql 复制代码
-- 大数据集性能对比(假设7天内有1000万条记录)

-- 方式1:HYPERLOGLOG(推荐大数据集)
SELECT HYPERLOGLOG(device_id) FROM meters WHERE ts >= NOW() - 7d;
-- 执行时间:约50ms,内存使用:1.5KB,结果:9987

-- 方式2:嵌套查询精确计数(小数据集推荐)  
SELECT COUNT(*) FROM (
    SELECT DISTINCT device_id FROM meters WHERE ts >= NOW() - 7d
);
-- 执行时间:约5000ms,内存使用:几百MB,结果:10000

-- 误差分析
SELECT 
    nest_count.exact_count,
    hll_count.approx_count,
    ABS(nest_count.exact_count - hll_count.approx_count) AS difference,
    ABS(nest_count.exact_count - hll_count.approx_count) * 100.0 / nest_count.exact_count AS error_rate
FROM (
    SELECT COUNT(*) AS exact_count FROM (
        SELECT DISTINCT device_id FROM meters WHERE ts >= NOW() - 7d
    )
) nest_count,
(
    SELECT HYPERLOGLOG(device_id) AS approx_count FROM meters WHERE ts >= NOW() - 7d
) hll_count;
-- 结果示例:exact_count=10000, approx_count=9987, difference=13, error_rate=0.13%

8. 使用注意事项

8.1 语法限制

sql 复制代码
-- ❌ 错误用法
SELECT HYPERLOGLOG() FROM meters;                    -- 无参数
SELECT HYPERLOGLOG(device_id, location) FROM meters; -- 多参数
SELECT HYPERLOGLOG(DISTINCT device_id) FROM meters;  -- 包含DISTINCT
SELECT COUNT(DISTINCT device_id) FROM meters;        -- TDengine不支持

-- ✅ 正确用法
SELECT HYPERLOGLOG(device_id) FROM meters;           -- 单参数
SELECT HYPERLOGLOG(voltage) FROM meters;             -- 数值类型
SELECT HYPERLOGLOG(location) FROM meters;            -- 字符串类型
SELECT HYPERLOGLOG(ts) FROM meters;                  -- 时间戳类型

8.2 场景选择建议

需求 推荐方案 理由
快速了解数量级 HYPERLOGLOG 高性能,低内存
需要具体值列表 SELECT DISTINCT 返回具体内容
精确计数(小数据集) 嵌套查询 100%准确
实时监控指标 HYPERLOGLOG 响应快速
报表统计 根据精度要求选择 平衡性能与精度

8.3 性能优化建议

sql 复制代码
-- 推荐:合理限制查询范围
SELECT HYPERLOGLOG(device_id) FROM meters 
WHERE ts >= NOW() - 24h AND location = 'California.SanFrancisco';

-- 推荐:结合其他聚合函数
SELECT 
    location,
    COUNT(*) AS total_records,
    HYPERLOGLOG(device_id) AS unique_devices,
    AVG(power) AS avg_power
FROM meters 
WHERE ts >= NOW() - 7d
GROUP BY location;

9. 总结

HYPERLOGLOG 函数与 SELECT DISTINCT 在智能电表监控系统中各有优势:

HYPERLOGLOG 优势

  1. 高性能:固定内存使用,适合大数据集
  2. 实时性:快速响应,适合监控指标
  3. 聚合友好:天然支持分组统计
  4. 分布式支持:易于分布式计算
  5. 算法先进:基于概率算法,理论基础扎实

SELECT DISTINCT 优势

  1. 精确性:返回100%准确的具体值
  2. 直观性:结果直观易理解
  3. 灵活性:可用于进一步的数据处理

选择建议

  • 监控指标、趋势分析:使用 HYPERLOGLOG
  • 具体值查询、小数据集:使用 SELECT DISTINCT + 嵌套查询
  • 报表统计:根据精度要求和性能需求选择
  • 大数据集分析:优先使用 HYPERLOGLOG

通过理解 HyperLogLog 算法的原理和特点,结合实际业务需求,可以在保证分析效果的同时最大化系统性能,为智能电表监控系统提供高效的去重统计能力。

关于 TDengine

TDengine 是一款专为物联网、工业互联网等场景设计并优化的大数据平台,其核心模块是高性能、集群开源、云原生、极简的时序数据库。

它能安全高效地将大量设备每天产生的高达 TB 甚至 PB 级的数据进行汇聚、存储、分析和分发,并提供 AI 智能体对数据进行预测与异常检测,提供实时的商业洞察。

相关推荐
Elastic 中国社区官方博客3 小时前
使用 Elasticsearch 构建 AI Agentic 工作流
大数据·数据库·人工智能·elasticsearch·搜索引擎·ai·全文检索
kobe_OKOK_3 小时前
django 使用绑定多个数据库实现数据的同步
数据库·python·django
麦兜*4 小时前
Spring Boot 项目 Docker 化:从零到一的完整实战指南
数据库·spring boot·redis·后端·spring·缓存·docker
zl9798994 小时前
MySQL-数据库日志
数据库·mysql·database
koping_wu4 小时前
【分布式】分布式ID生成方案、接口幂等、一致性哈希
分布式·算法·哈希算法
泊浮目4 小时前
AutoMQ代码里的那些设计
大数据·消息队列
七夜zippoe4 小时前
微服务配置中心高可用设计:从踩坑到落地的实战指南(一)
java·数据库·微服务
重启的码农4 小时前
kv数据库-leveldb (11) 版本集 (VersionSet / Version)
数据库