土法炼钢 · 系统与基础设施

【列存引擎内核】Merge 与 Mutation

文章导航

分类入口
databasestorage
标签入口
#clickhouse#merge#mutation#replacingmergetree#collapsingmergetree#background-merge#24-lts

目录

Insert 只 追加 Part;后台 merge 把多个小 Part 归并为更大 Part——列存的「心脏手术」,类比 LSM CompactionMutation 则异步重写 Part 实现 UPDATE/DELETE,堆积会拖垮集群。


一、为何需要 Merge

无 merge 有 merge
Part 数线性涨 Part 数受控
每次查询打开大量目录 读放大下降
parts_to_throw_insert 拒绝写 可持续 ingest
flowchart LR
  P1[Part 小] --> M[Merge]
  P2[Part 小] --> M
  M --> P3[Part 大]

二、Merge 触发与 selector

MergeTreeBackgroundExecutor 调度任务;selector 根据 Part 大小、level、分区挑选可 merge 组合(SimpleMergeSelector 等)。

Settings:max_bytes_to_merge_at_max_space_in_poolmerge_max_block_sizenumber_of_free_entries_in_pool_to_execute_merge

观察(本机):

SELECT * FROM system.merges;
SELECT database, table, sum(1) FROM system.parts WHERE active GROUP BY database, table;

不伪造 system.merges 行。


三、Merge 做什么

对同分区、排序键兼容的 Part 多路归并(与 LSM 归并相似):

源码:MergeTreeDataMergerMutator.cpp


四、ReplacingMergeTree

CREATE TABLE r (
  id UInt64,
  v String,
  ver UInt64
) ENGINE = ReplacingMergeTree(ver)
ORDER BY id;

Merge 同 id 保留 ver 最大行。查询可能见重复,除非 FINAL(有性能成本)。


五、CollapsingMergeTree

Sign Int8:+1 插入,-1 撤销;merge 折叠。适合变更流。


六、Mutation

ALTER TABLE UPDATE v = ... WHERE ... → 队列 → 读旧 Part 写新 Part。

危害:

生产尽量避免大范围 mutation;用 Replacing 或重建表替代。

stateDiagram-v2
  [*] --> Queued: ALTER UPDATE
  Queued --> Executing: background
  Executing --> Done: 新 Part 替换
  Executing --> Failed: 磁盘/超时

七、TTL

TTL 规则在 merge 时删除/移动过期 granule,依赖 merge 运行。


八、学术谱系:RS Merge · LSM Compaction · Mutation

阶段 文献 / 系统 本篇落点
2005 C-Store RS 批量归并只读存储 MergeTree 后台 merge 归并 Part
1996+ LSM compaction(O’Neil;本站 lsm-tree 类比:小文件 → 大文件;差异:列存按排序键归并列文件,非 InternalKey 多版本
CH Replacing / Collapsing 等 merge 时应用引擎语义
CH Mutation 异步重写 Part ≈「强制昂贵 update」

争论:用 merge 消化变更(类 LSM / RS)vs 原地更新(行存)。列存选前者换 scan;代价是 parts 过多、mutation 堆积(第 15 篇)。

8.1 工程间隙

论文 / LSM 叙述 MergeTree
写放大公式清晰 merge selector 启发式 + 磁盘/副本争抢,难用单公式预测
Compaction 与前台写 stall 模型成熟(RocksDB) CH 有 parts 限流,但运维信号在 system.merges / parts 计数
少谈跨副本 ReplicatedMergeTree 上 merge 与 fetch 交织(第 8 篇)

8.2 开放问题

  1. Mutation 是否应默认禁止于大表? 可检验:同等更新用 Collapsing/Replacing ingest vs ALTER UPDATE 的 parts 与合并时长。
  2. Merge 与查询的 CPU/IO 隔离——多租户下仍是生产痛点;论文实验少覆盖。

九、小结

Merge = 归并 Part、控制读放大、实现引擎语义;Mutation = 重写 Part、应慎用。


上一篇查询读取路径

下一篇索引与跳数索引


参考资料

核心论文

  1. Stonebraker et al., C-Store, VLDB 2005(RS 归并直觉;A 级)。
  2. O’Neil et al., The Log-Structured Merge-Tree, Acta Informatica 1996(compaction 对照;A 级)。

规范 / 源码 / 文档

  1. ClickHouse Documentation, ReplacingMergeTreeCollapsingMergeTreeMutations(A 级)。
  2. ClickHouse Source, MergeTreeDataMergerMutator.cppMergeTreeBackgroundExecutor.cpp(A 级)。
  3. LSM 系列, Compaction

Merge selector

SimpleMergeSelector / TTLMergeSelector 等根据 Part 大小、年龄、分区挑选 merge 任务;目标减少 Part 数且控制写放大。

ReplacingMergeTree

replace 版本列默认 is_deleted 或显式 version;merge 同排序键保留 version 最大。查询仍可能见重复行,除非 FINAL 或应用层去重。

CollapsingMergeTree

Sign 列 +1/-1 表示插入与撤销;merge 折叠成 net 行。适合变更流而非物理 DELETE。

Mutation 路径

ALTER TABLE UPDATE/DELETEMutationCommands 队列 → 后台读 Part 重写新 Part → 原子替换。堆积时 system.mutations 可见;与 merge 争用 BackgroundSchedulePool

读完这篇,下一步读什么

优先读同系列或同问题的下一篇,把单篇消费变成主题集群。

2026-06-18 · database / storage

【列存引擎内核】列存基础与 ClickHouse 架构

行存 vs 列存的带宽、压缩与向量化三角;ClickHouse Server 进程模型、线程池与 MergeTree 引擎家族地图;src/Storages 与 src/Processors 源码入口。对照 PG 行存与 LSM 写优化路径,版本锚定 ClickHouse 24.x LTS。

2026-06-18 · database / storage

【列存引擎内核】MergeTree Part 文件格式

ClickHouse MergeTree Part 目录结构:columns.txt、checksums.txt、.bin、.mrk2、primary.idx 语义,Granule 与 Mark 的定位作用,Wide/Compact 布局与 MergeTreeDataPart 源码入口。版本锚定 24.x LTS。

2026-06-18 · database / storage

【列存引擎内核】压缩与编码

ClickHouse 列压缩:LZ4、ZSTD、Delta、DoubleDelta、Gorilla 时序编码与列类型关系;CODEC 链顺序、LowCardinality 与 PG TOAST 对照。压缩比须本机实测,本文不编造倍数。

2026-06-18 · database / storage

【列存引擎内核】向量化执行引擎

ClickHouse Block 列向量 batch、IProcessor Pipeline 与 filter/project/aggregate 向量实现;对照 PostgreSQL 火山模型 ExecProcNode。源码入口 src/Processors、src/Columns。24.x LTS。


By .