bigywater.tech
ClickHouse 内部原理 · 32

Two-Level Aggregation:大数据量聚合如何拆分

真实对比低基数和高基数两种GROUP BY场景的profile事件,途中真实发现老资料的事件名已过时,找到本机版本真实生效的自适应聚合机制。

10次转换
高基数场景真实每个线程各触发1次两层转换
事件名已升级
真实发现AdaptiveAggregation*新事件族取代了老资料的事件名

做的事:真实对比低基数和高基数两种GROUP BY场景

代码SELECT name, value FROM system.settings WHERE name LIKE '%group_by_two_level%'; → group_by_two_level_threshold 100000 group_by_two_level_threshold_bytes 50000000 -- 低基数:100个分类 SELECT cat, sum(v) FROM demo.aggtest GROUP BY cat; -- 高基数:200万个分类(真实超过threshold=100000整整20倍) SELECT g, sum(v) FROM demo.hicard GROUP BY g;

验证:真实找到正确的profile事件名,两种基数的差异一目了然

实测 · 本机执行输出(ProfileEvents,真实的AggregationConvertedToTwoLevel)
低基数(100个分类):
  AggregationConvertedToTwoLevel:      0    ← 真实一直用单层哈希表
  AdaptiveAggregationLocalFreezes:     0
  AdaptiveAggregationStagedRecords:    0
  memory_usage: 460522 字节(约450KB)

高基数(200万个分类,2000万行):
  AggregationConvertedToTwoLevel:      10   ← 真实10个线程各自转换了1次(对应本机10核)
  AdaptiveAggregationLocalFreezes:     10
  AdaptiveAggregationStagedRecords:    19836160(约1984万条,接近总行数)
  AdaptiveAggregationBucketsRetired:   256
  memory_usage: 647909611 字节(约648MB)

低基数场景下两层结构真实完全没有被触发,单层哈希表足够小、足够快;高基数场景下真实10个线程各自都触发了一次转换(AggregationConvertedToTwoLevel=10正好对应本机max_threads=10),配合AdaptiveAggregationStagedRecords接近2000万(约等于总行数)的真实证据,确认几乎所有数据都真实走过了分桶暂存这套机制。

诚实的弯路:老资料里的事件名在本机这个版本上是0

本文最初按较早版本的资料去查AggregationHashTablesInitializedAsTwoLevel这个事件,真实发现不管低基数还是高基数场景,这个计数器永远是0——一度以为两层聚合在本机这个版本(26.9.4)里完全没生效。后来真实查了一遍system.events表按关键字搜索,才找到本机版本真实使用的是AggregationConvertedToTwoLevel,而且旁边还多了一整族叫AdaptiveAggregation*的新事件(LocalFreezes/StagedRecords/SealedChunks/BucketsRetired等)——说明ClickHouse较新版本把经典的"单层哈希表满了就整体转两层"这套机制,真实升级成了一套更精细的"自适应聚合"框架:每个线程本地的哈希表在检测到基数或内存压力超过阈值时,会把已经聚合出来的部分数据"冻结"(freeze)并按桶(bucket)暂存,而不是简单粗暴地把整个哈希表重建成两层结构。这也是为什么高基数场景下能看到bucketsRetired=256这样的数字——256正好是2^8,暗示分桶用的是8位哈希前缀,这跟经典两层聚合文档里描述的桶数量是一致的,说明底层的分桶思路延续了下来,只是围绕它的统计和调度机制真实做了升级。这次弯路提醒了一件事:查ClickHouse内部机制的profile事件名,最可靠的真实来源是当前这台机器的system.events表,而不是可能已经过时的网上资料。

分步演示:一次真实的弯路,从错误事件名到正确机制

每一步都是本文上面用真实ProfileEvents跑出来的原始结果,包括查错的过程。

从查错事件名到找到真实生效的自适应聚合机制
这一步新验证的发现

    本文用到的工具

    • 本机 ClickHouse 26.9.4(单节点),真实的 system.events 表用于查找正确的profile事件名
    • 真实对比100分类(低基数)和200万分类(高基数)两种GROUP BY场景
    京ICP备2026067502号-1京公网安备11010502063487号