StarRocks ds_hll_accumulate 深度解析:DataSketches HyperLogLog 近似去重聚合函数
2026/9/17 22:35:31 网站建设 项目流程

StarRocks ds_hll_accumulate 深度解析:DataSketches HyperLogLog 近似去重聚合函数

【免费下载链接】starrocksThe world's fastest open query engine for sub-second analytics both on and off the data lakehouse. With the flexibility to support nearly any scenario, StarRocks provides best-in-class performance for multi-dimensional analytics, real-time analytics, and ad-hoc queries. A Linux Foundation project.项目地址: https://gitcode.com/GitHub_Trending/st/starrocks

本文围绕 StarRocks 聚合函数ds_hll_accumulate展开,讲清它的语法、参数语义、返回类型与完整用法示例,并深入到 BE 侧DataSketchesHll的源码实现,解释log_k精度控制、HLL_4/6/8目标类型以及序列化/反序列化链路的工作原理。读完之后,你将能够把任意列的值累积为可序列化、可合并、可跨分区/跨节点聚合的 HLL 草图(sketch),并基于ds_hll_combineds_hll_estimate完成大规模数据的近似去重统计。

函数定位:DataSketches HLL 函数族的"累积器"

ds_hll_accumulate的作用是把输入表达式的值累积进一个 HyperLogLog 草图,并返回序列化后的草图二进制(VARBINARY)。它是 StarRocks 基于 Apache DataSketches 实现的 HLL 近似去重函数族中的一员,与ds_hll_combineds_hll_estimateds_hll_count_distinct配合形成完整的"累积 → 合并 → 估算"工作流:

  • ds_hll_accumulate:将原始值累积进 HLL 草图,产出 VARBINARY 序列化结果;
  • ds_hll_combine:把多个已序列化的草图合并成一个草图(见 ds_hll_combine 文档);
  • ds_hll_estimate:从序列化草图中估算去重数量(见 ds_hll_estimate 文档);
  • ds_hll_count_distinct:一步到位的近似去重计数函数,直接返回估计值(见 ds_hll_count_distinct 文档)。

这种"先累积出可存储、可传输的草图对象"的设计,使得 HLL 草图可以作为中间状态落表、跨批次或跨节点传递,再进行 union 合并与基数估算,这是它与直接返回 BIGINT 的ds_hll_count_distinct最本质的区别。

语法与参数

官方语法(继承自 ds_hll_accumulate 文档):

sketch ds_hll_accumulate(expr) sketch ds_hll_accumulate(expr, log_k) sketch ds_hll_accumulate(expr, log_k, tgt_type)

参数说明如下:

参数类型/取值默认值说明
expr任意数据类型要累积进草图的表达式,列、字面量或表达式均可
log_k整数,范围[4, 21]17控制草图的精度与内存占用:log_k越大,寄存器越多,精度越高,内存也越大
tgt_typeHLL_4/HLL_6/HLL_8HLL_6目标 HLL 类型,决定草图寄存器位宽

从源码可以印证这些默认值。BE 侧的常量定义在 be/src/types/constexpr.h 中:DEFAULT_HLL_LOG_K = 17(第 32 行);而 be/src/exprs/agg/data_sketch/ds_hll.h 中明确写出static const datasketches::target_hll_type DEFAULT_HLL_TGT_TYPE = datasketches::HLL_6;,即缺省目标类型为HLL_6。FE 侧在 fe/fe-core/src/main/java/com/starrocks/catalog/FunctionSet.java 中注册了三个重载(单参、expr+INTexpr+INT+VARCHAR),均返回 VARBINARY、中间聚合状态也为 VARBINARY,说明该函数支持多阶段聚合(partial/final)——草图本身即可作为聚合中间状态在节点间流转。

返回类型与典型使用方式

ds_hll_accumulate返回 VARBINARY,内容是 DataSketches 的 HLL 草图序列化字节流。典型用法分两类:

  1. 作为聚合中间结果落表:在GROUP BY中按维度生成草图列存入库,后续对同一维度不同分区的草图用ds_hll_combine合并,再用ds_hll_estimate求基数;
  2. 临时验证:直接查看某列的草图对象,或配合ds_hll_estimate快速得到近似去重数。

完整示例(继承自官方文档,可直接运行):

-- 建测试表 CREATE TABLE t1 ( id BIGINT, province VARCHAR(64), age SMALLINT, dt VARCHAR(10) ) DUPLICATE KEY(id) DISTRIBUTED BY HASH(id) BUCKETS 3; -- 插入测试数据 INSERT INTO t1 SELECT generate_series, generate_series, generate_series % 100, "2024-07-24" FROM table(generate_series(1, 1000)); -- 基本用法:单参数 SELECT ds_hll_accumulate(id) FROM t1; -- 自定义 log_k SELECT ds_hll_accumulate(province, 20) FROM t1; -- 同时指定 log_k 与 tgt_type SELECT ds_hll_accumulate(age, 12, "HLL_6") FROM t1; -- GROUP BY 用法 SELECT dt, ds_hll_accumulate(id), ds_hll_accumulate(province, 20), ds_hll_accumulate(age, 12, "HLL_6"), ds_hll_accumulate(dt) FROM t1 GROUP BY dt ORDER BY 1 LIMIT 3;

几点实操注意:

  • 草图参数必须与后续ds_hll_combine/ds_hll_estimate中草图的log_k一致,合并不同log_k的草图会失败或产生错误结果,因此建议整条链路固定log_k
  • tgt_type不同(HLL_4位宽最小、HLL_8最大)的草图同样不宜混用,默认HLL_6是精度与体积的平衡选择;
  • 返回的 VARBINARY 是二进制 blob,如需肉眼查看可结合hex()等字符串函数(以实际版本函数支持为准)。

源码级实现解析

ds_hll_accumulate的 BE 实现核心是 be/src/exprs/agg/data_sketch/ds_hll.h 与 be/src/exprs/agg/data_sketch/ds_hll.cpp 中的DataSketchesHll类,它包装了 DataSketches C++ 库的hll_sketch_alloc/hll_union_alloc。几个关键机制值得了解:

1. 输入经过哈希后喂给 union。update(uint64_t hash_value)的注释明确要求输入必须是哈希值:

// Add a hash value to this HLL value // NOTE: input must be a hash_value void update(uint64_t hash_value);

也就是说聚合框架会先把expr的值哈希成 64 位整数,再调用_sketch_union->update(hash_value)累加。这解释了为什么expr可以是任意数据类型——任何类型都能被统一哈希。

2. 累积器内部是一个 union 对象,草图惰性求值。DataSketchesHll持有hll_union_type成员,get_hll_sketch()_is_changed标记,只有在状态变化后真正需要结果时才通过_sketch_union->get_result(_tgt_type)物化出hll_sketch。序列化时调用serialize_compact(),即以紧凑格式输出草图字节流。

3. 反序列化有严格的前缀校验。is_valid()检查序列化数据第一个字节是否为HLL_PREINTSHASH_SET_PREINTSLIST_PREINTS三种合法前缀之一,防止把非法二进制误当草图处理;deserialize()会重建hll_union并把读入的草图 union 进去,这是ds_hll_combine等"从 VARBINARY 恢复草图再合并"能力的底层基础。

4. 基数估算采用非 HIP 估计器。estimate_cardinality()的源码注释说明:它刻意使用 composite(非 HIP,Historic Inverse Probability)估计器,以避免 union 内部乱序标记未设置时 HIP 累积器带来的偏差——这属于ds_hll_estimate侧的细节,但保证了累积→合并→估算全链路的估计一致性。

5. 内存可计量。类内部使用STLCountingAllocator<uint8_t>计数分配器并暴露mem_usage(),说明草图的内存开销被纳入 BE 的内存计量体系,log_k调大时草图体积的增长是可观测的。

此外,FE 解析层在 fe/fe-core/src/main/java/com/starrocks/sql/parser/SyntaxSugars.java 中还提供了一个兼容糖:approx_count_distinct_hll_sketch(col)会被改写为ds_hll_count_distinct(col),方便从旧版 HLL 函数命名迁移过来的 SQL 平滑运行。

与相近函数的选择建议

  • 只需要最终去重数、不需要复用中间草图:直接用ds_hll_count_distinct(col[, log_k[, tgt_type]]),一步到位返回 BIGINT;
  • 需要跨分区/跨天/跨节点合并去重(例如按天存草图、月末合并出全局 DAU):用ds_hll_accumulate产草图落表 +ds_hll_combine合并 +ds_hll_estimate估算;
  • 追求更高的估计质量且数据允许:可关注同族的ds_theta_*系列函数(仓库文档同目录下提供了ds_theta_accumulateds_theta_combineds_theta_count_distinct等),Theta 草图在支持交集等更复杂集合运算上更有优势。

小结

ds_hll_accumulate是 StarRocks 中"把列数据沉淀为可复用 HLL 草图"的入口函数:默认log_k=17tgt_type=HLL_6的参数语义在 BE 源码(DEFAULT_HLL_LOG_KDEFAULT_HLL_TGT_TYPE)中有明确对应;其内部经由哈希累积、union 合并、紧凑序列化三套机制,保证了草图在多阶段聚合与跨实例流转中的正确性。理解"expr 任意类型 → 哈希值 → HLL union → 紧凑序列化"这条链路后,你就能放心地把它用进按天落草图、周期性合并估算的大规模近似去重场景。

【免费下载链接】starrocksThe world's fastest open query engine for sub-second analytics both on and off the data lakehouse. With the flexibility to support nearly any scenario, StarRocks provides best-in-class performance for multi-dimensional analytics, real-time analytics, and ad-hoc queries. A Linux Foundation project.项目地址: https://gitcode.com/GitHub_Trending/st/starrocks

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询