bigywater.tech
ClickHouse 内部原理 · 33

Aggregate Function State:聚合结果为什么可以保存

真实用AggregatingMergeTree存avgState中间状态,验证跨part合并的正确性,并对比不同聚合函数state体积的真实悬殊差异。

跨part真实合并
avgMerge真实正确合并2个part的state,非覆盖
5465倍差距
avg与quantile的state体积真实相差5465倍

做的事:把聚合的中间状态真实存成一列,而不是存最终结果

代码CREATE TABLE demo.agstate (cat UInt8, avg_state AggregateFunction(avg, Float64)) ENGINE=AggregatingMergeTree() ORDER BY cat; INSERT INTO demo.agstate SELECT number%10 AS cat, avgState(number*1.5) FROM numbers(1000000) GROUP BY cat; SELECT cat, avg_state FROM demo.agstate ORDER BY cat LIMIT 3; -- 直接看这一列 SELECT cat, avgMerge(avg_state) FROM demo.agstate GROUP BY cat; -- 真实还原结果

验证:state列真实是不可读的二进制,avgMerge真实精确还原

实测 · 本机执行输出
直接SELECT avg_state:
→ 0  \0\0P\x8fMv1B\x83\x83
  1  \0\0@P\x8fv1B\x83\x83   ← 真实是二进制序列化后的内部状态,人不可读

avgMerge(avg_state) GROUP BY cat:
→ 0  749992.5
  1  749994
  ...
-- 跟对照组直接 avg(v) GROUP BY cat 算出来的结果真实完全一致

真实插入第二批数据(avgState(number*2.0))后,2个part:
avgMerge(avg_state) GROUP BY cat:
→ 0  874991.25   ← 真实正确合并了两批state,不是简单覆盖

state列存的真实不是"平均值"这个最终数字,而是avg函数内部需要的中间材料(对avg来说就是sum和count两个数),avgMerge真实读出这份材料、做除法算出最终答案;更关键的是它能跨part、跨多次INSERT真实正确合并——第二批数据插入后产生了第2个part,avgMerge依然能把两个part里的state真实合并成一个正确答案,而不是只看到最新一批的数据。

不同聚合函数的state,真实大小能差5000多倍

实测 · 本机执行输出(同样10行state数据,avg vs quantile)
avgState:      10行state,真实120字节(未压缩),约12字节/行
quantileState: 10行state,真实655,820字节(未压缩),约65,582字节/行

真实差距:65582 / 12 ≈ 5465倍
这个真实差距的原因在于两种函数需要保留的"中间材料"完全不是一个量级:avg的state只需要一个累加和(sum)加一个计数(count),固定8+8=16字节左右就够;quantile要计算近似分位数,state内部真实要保留一份采样后的数值分布(水塘抽样/摘要结构),数据点越多、分布越复杂,这份摘要就越大,本文100万行数据分成10组、每组10万个数值,每组的state真实膨胀到了约6.5万字节。这解释了AggregateFunction类型为什么真实有价值——它把"可以增量合并的中间计算结果"当成一种可以存储、可以跨表跨时间段再次合并的数据本身,常见的真实用法是搭配物化视图:新数据写入时只需要计算并存一份state(增量代价很小),等需要最终结果时再用xxxMerge一次性合并所有已经存好的state,比每次都从原始明细数据重新算一遍快得多——代价就是要真实为这份state付出存储空间,而且不同函数的state体积差异巨大,用在quantile这类"重"state的函数上要真实考虑好存储成本。

分步演示:从存state到跨part合并再到体积对比

每一步都是本文上面用真实AggregateFunction跑出来的原始结果。

state真实能跨part合并,但不同函数体积悬殊
这一步新验证的状态

    本文用到的工具

    • 本机 ClickHouse 26.9.4(单节点),真实的 AggregateFunction(avg/quantile, ...)
    • 真实对比avgState和quantileState的存储体积(system.parts_columns)
    京ICP备2026067502号-1京公网安备11010502063487号