
StarRocks percentile_hash 函数详解将 DOUBLE 值构造为 PERCENTILE 近似分位数【免费下载链接】starrocksThe worlds 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/starrockspercentile_hash是 StarRocks 中用于把 DOUBLE 类型的原始数值构造成PERCENTILE聚合类型的核心构造函数是 PERCENTILE 类型在写入、聚合与物化视图等场景中的入口。本文以官方文档 percentile_hash.md 为主体结合 BE 端源码与配套函数文档完整讲解其语法、参数、返回类型、实战用法与底层 T-Digest 实现原理帮助你在聚合表、Stream Load 和物化视图中正确使用 PERCENTILE 做近似分位数统计。函数定位PERCENTILE 类型的唯一入口在 StarRocks 中PERCENTILE不是普通标量类型而是一种用于近似分位数quantile/percentile统计的聚合载体类型。原始 DOUBLE 数值不能直接存入 PERCENTILE 列必须先经过percentile_hash将其哈希进一个分位数数据结构中。提示这里的 hash 并不是密码学意义上的哈希摘要而是把单个 DOUBLE 值装载进基于 T-Digest 的PercentileValue结构见 percentile_value.h后续所有分位数计算都基于该结构进行。因此文档描述将其定义为 Constructs DOUBLE values as PERCENTILE values把 DOUBLE 值构造成 PERCENTILE 值。语法与参数说明PERCENTILE_HASH(x);参数参数说明x需要构造成 PERCENTILE 的输入值仅支持 DOUBLE 类型返回值返回一个PERCENTILE类型的值其中承载了输入值及其权重信息供后续percentile_union、percentile_approx_raw等函数使用。空值处理从 BE 端实现percentile_functions.cpp可以看到函数逐行遍历输入列当某一行值为 NULL 时会写入一个空的PercentileValue等价于不包含任何数据而不会报错或跳过整行ColumnViewerTYPE_DOUBLE viewer(columns[0]); auto percentile_column PercentileColumn::create(); size_t size columns[0]-size(); for (int row 0; row size; row) { PercentileValue value; if (!viewer.is_null(row)) { value.add(viewer.value(row)); } percentile_column-append(value); }同时实现中还做了常量列优化当整列输入都是常量时函数返回ConstColumn包装的结果避免不必要的逐行计算开销。官方示例与结果解读官方文档给出的最小示例是直接在percentile_hash的结果上调用percentile_approx_raw求 0.99 分位数mysql select percentile_approx_raw(percentile_hash(234.234), 0.99); ------------------------------------------------------- | percentile_approx_raw(percentile_hash(234.234), 0.99) | ------------------------------------------------------- | 234.23399353027344 | ------------------------------------------------------- 1 row in set (0.00 sec)注意输出值234.23399353027344与输入234.234存在极微小的偏差这正是 T-Digest 近似算法的特征PercentileValue内部以浮点型float存储质心均值见 tdigest.h 中using Value float;分位数查询quantile(q)也是基于压缩后的摘要结构估算而非精确排序。因此percentile_hash适用于海量数据的近似分位数统计对单值精度要求苛刻的场景如财务精确计算应改用精确类型。实战场景一聚合表中的 PERCENTILE 列PERCENTILE 类型最典型的用法是配合聚合模型Aggregate Key表以PERCENTILE_UNION作为聚合方式。参考配套文档 percentile_approx_raw.md建表与写入流程如下CREATE TABLE aggregate_tbl ( site_id largeint(40) NOT NULL COMMENT id of site, date date NOT NULL COMMENT time of event, city_code varchar(20) NULL COMMENT city_code of user, pv bigint(20) SUM NULL DEFAULT 0 COMMENT total page views, percent PERCENTILE PERCENTILE_UNION COMMENT others ) ENGINEOLAP AGGREGATE KEY(site_id, date, city_code) COMMENT OLAP DISTRIBUTED BY HASH(site_id) PROPERTIES (replication_num 1);写入时每条记录的原始数值都必须用percentile_hash包装insert into aggregate_tbl values (5, 2020-02-23, city_code, 555, percentile_hash(1)); insert into aggregate_tbl values (5, 2020-02-23, city_code, 555, percentile_hash(2)); insert into aggregate_tbl values (5, 2020-02-23, city_code, 555, percentile_hash(3)); insert into aggregate_tbl values (5, 2020-02-23, city_code, 555, percentile_hash(4));随后即可查询任意分位数mysql select percentile_approx_raw(percent, 0.5) from aggregate_tbl; ------------------------------------- | percentile_approx_raw(percent, 0.5) | ------------------------------------- | 2.5 | ------------------------------------- 1 row in set (0.03 sec)多条记录在导入落盘时按PERCENTILE_UNION聚合底层通过PercentileValue::merge将多个 T-Digest 合并为一份摘要见 percentile_value.h因此无论聚合多少数据存储的始终是压缩后的分位数摘要而非全量明细这就是 PERCENTILE 类型能支撑超大规模分位数统计的原因。实战场景二Stream Load 导入时转换在通过 Stream Load 导入包含 PERCENTILE 列的数据时同样需要借助percentile_hash做列转换。参考 percentile_union.md 的示例建一张包含 PERCENTILE 列的聚合表CREATE TABLE sales_records( record_id int, seller_id int, store_id int, sale_amt_per percentile percentile_union ) ENGINEOLAP AGGREGATE KEY(record_id, seller_id, store_id) COMMENT OLAP DISTRIBUTED BY HASH(record_id) PROPERTIES ( replication_num 3, storage_format DEFAULT );导入时先用临时列tmp接收原始 DOUBLE 值再通过sale_amt_per percentile_hash(tmp)完成类型转换curl --location-trusted -u root -H columns: record_id, seller_id, store_id,tmp, sale_amt_per percentile_hash(tmp) -H column_separator:, -T a http://ip:port/api/test/sales_records/_stream_load查询时配合percentile_union与percentile_approx_raw还原分位数结果select percentile_approx_raw(percentile_union(sale_amt_per), 0.99) from sales_records;实战场景三物化视图中的预聚合percentile_hash也可以出现在物化视图的建视图语句中实现明细表 分位数物化视图的组合。仍参考 percentile_union.mdCREATE TABLE sales_records( record_id int, seller_id int, store_id int, sale_date date, sale_amt bigint ) distributed BY hash(record_id) PROPERTIES (replication_num 3);create materialized view mv as select store_id, percentile_union(percentile_hash(sale_amt)) from sales_records group by store_id;这里percentile_hash先把每个sale_amt明细值转换为 PERCENTILEpercentile_union再按store_id分组聚合成压缩摘要从而让分位数查询命中物化视图加速。底层原理从 T-Digest 到 PERCENTILE 列数据类型链PERCENTILE 相关函数在 BE 端的声明位于 percentile_functions.h三者构成了完整的数据流转闭环函数输入输出职责percentile_hashDOUBLEPERCENTILE构造单值 → 摘要percentile_unionPERCENTILEPERCENTILE聚合多摘要 → 一份摘要percentile_approx_rawPERCENTILE, DOUBLEDOUBLE查询摘要 → 指定分位数估值此外还有 percentile_empty用于在 Stream Load / INSERT INTO 导入时填充 NULL 占位。PercentileValue 与序列化PercentileValue是 PERCENTILE 值的内存表示percentile_value.h内部持有TDigest _tdigest与 1 字节的_type标记当前仅支持TDIGEST一种实现序列化格式为1 字节类型标记 T-Digest 序列化数据落盘与网络传输都基于该格式add(value)等价于_tdigest.add(value)quantile(q)委托给_tdigest.quantile(q)。T-Digest 近似算法percentile_hash之所以能支撑海量数据的实时分位数统计根源在于 T-Digest 数据结构。StarRocks 的实现tdigest.h基于 Ted Dunning 与 Otmar Ertl 的论文《Computing extremely accurate quantiles using t-digest》及 derrickburns/tdigest 开源实现其核心思路是用一组带权重的质心Centroid描述数据分布靠近尾部分布低分位/高分位的质心更密集从而在压缩存储的同时保留对极端分位数的高精度通过kHighWater 40000等阈值控制质心数量与压缩策略平衡内存占用与精度merge操作天然支持分片/副本间摘要合并与percentile_union的聚合语义完全吻合。因此percentile_hash得到的 PERCENTILE 值并非原始数据快照而是一份可合并、可序列化、可查询的近似分布摘要——这正是示例中234.234被估算为234.23399353027344的原因也是官方文档将其归类为近似分位数函数族的定位所在。注意事项仅支持 DOUBLE 输入percentile_hash的参数类型限定为 DOUBLE其他数值类型需要先显式转换为 DOUBLE 再传入。近似而非精确结果基于 T-Digest 估算存在微小误差需要精确分位数如精确中位数时应使用percentile_cont/percentile_disc等精确函数。与聚合类型绑定PERCENTILE 列只能定义在聚合模型Aggregate Key表中且聚合方式必须为PERCENTILE_UNION。分位数取值区间配合percentile_approx_raw查询时分位数参数y的取值范围为[0.0, 1.0]例如 0.99 表示 99 分位。NULL 输入对 NULL 值percentile_hash会写入空摘要查询时该行不参与分位数统计。参考文档与源码函数官方文档percentile_hash.md配套查询函数percentile_approx_raw.md配套聚合函数percentile_union.md空值占位函数percentile_empty.mdBE 端函数实现percentile_functions.cppPERCENTILE 值类型percentile_value.hT-Digest 实现tdigest.h聚合函数实现percentile_union.h【免费下载链接】starrocksThe worlds 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),仅供参考