5 个交互演示:① Replacing 去重时机 · ② Summing 自动汇总 · ③ Aggregating 状态合并 · ④ Collapsing 行折叠 · ⑤ FINAL vs argMax 对比
点「插入同主键」模拟对同一个 user_id 的多次更新,点「触发 Merge」让 ReplacingMergeTree 按 version 去重。
| user_id | nickname | version |
|---|
| user_id | nickname | version |
|---|
点「插入一行」模拟 (date, event, user) 相同的新事件,数值列 pv / revenue 在 Merge 时自动累加。
| date | event | user | pv | revenue |
|---|
| date | event | user | sum(pv) | sum(revenue) |
|---|
GROUP BY + sum(),不要依赖引擎"已经合并"。演示 uniqState(user_id) 写入时生成 HLL 状态(用 32 位"命中位"抽象),
Merge 时按位 OR 合并两个状态,最终用 uniqMerge 估算基数。
uniqMerge 估算独立用户数uniqState 是一个**定长状态**(这里简化成 32 位);
合并时按位 OR,不会再占更多空间;uniqMerge 基于命中位数估算基数(真正的 HLL 算法精度更高)。
这就是亿级明细能压成千级状态行的秘密。
演示订单状态翻转的三步:「创建(+1) → 撤销前态(-1) → 写新态(+1)」,Merge 时相邻 +1/-1 被消掉。
| order_id | status | amount | Sign |
|---|
| order_id | latest_status | live_amount | live_rows |
|---|
VersionedCollapsingMergeTree。
| 方式 | 实现 | 正确性 | 性能 | 何时用 |
|---|---|---|---|---|
SELECT ... FINAL |
查询时按主键做一次额外归并,把同主键多行按家族规则收敛 | ✅ 精确 | ❌ 慢 2-10 倍多一次 sort + merge 扫描 | 调试 / 低频一次性查询 |
argMax(col, ver) GROUP BY pk |
用聚合函数在查询时自己实现"取最新" | ✅ 精确 | ✅ 通常比 FINAL 快走正常聚合 pipeline | Replacing 高频查询首选 |
OPTIMIZE TABLE ... FINAL |
后台立刻执行一次全表合并,物理把所有 Part 压成一个 | ✅ 精确(合并完再查) | ❌❌ 巨贵可能跑几小时,阻塞 DDL,不能高频触发 | 仅离线:压缩历史 / 冷分区归档前 |
sum(col*Sign) GROUP BY pk HAVING sum(Sign)>0 |
Collapsing 家族通用查询套路 | ✅ 精确 | ✅ 正常聚合性能 | Collapsing / VersionedCollapsing 查询首选 |
xxMerge(xxState) GROUP BY pk |
Aggregating 家族通用查询套路 | ✅ 精确(HLL 等估算算法有精度) | ✅ 极快只扫聚合状态,毫秒级 | Aggregating 查询首选 |
| 查询 | read_rows | duration | 注 |
|---|---|---|---|
| SELECT count() FROM t | 10,000,000 | 120 ms | 基线 |
| argMax(name, ver) GROUP BY id | 10,000,000 | 650 ms | 1 次 scan + 聚合 |
| SELECT * FROM t FINAL | 10,000,000 | 3,200 ms | 5 倍于 argMax |
| OPTIMIZE TABLE t FINAL | 重写整表 | ~60 s | I/O 密集 |
| uniqMerge on AggregatingMT | 5,200 | 35 ms | 压到状态行 |