首页/数据/NoSQL 速查/ReplacingMergeTree 去重存储
ClickHouse56 条

ReplacingMergeTree 去重存储

ReplacingMergeTree 在合并时按排序键保留版本号最大的行,实现去重;重复消除发生在合并时且只在同分区内,查询未合并数据要用 FINAL 或聚合手段。

NoSQL 速查数据更新于 2026-10-13

ReplacingMergeTree 是什么

ReplacingMergeTree 是 ClickHouse MergeTree 家族中的一个表引擎,用于在后台合并 part 时按排序键去重、只保留版本最新的一行。它解决的是「同一实体反复写入、读取时只关心最新状态」这类问题:写入侧可以无脑追加变更记录,后台合并逐步收敛,读取侧用 FINAL 或手工聚合拿到去重结果。

基本信息

引擎参数ReplacingMergeTree([ver]),ver 为版本列,缺省时保留最后写入的那一行
去重范围同一数据分区内、排序键 ORDER BY 完全相同的行;跨分区不去重
去重时机后台合并时执行,不是写入即去重
读取保证SELECT ... FINAL 强制去重,或 GROUP BY 排序键 + argMax 手工取最新
复制版本ReplicatedReplacingMergeTree 用于副本表,去重语义相同

典型使用场景

  • CDC 增量同步:源库每条变更写一行,落库后合并自动收敛为最新快照
  • 维表全量刷新:每天重复导入同一批主键,读时只取版本最大的记录
  • 状态流转审计:保留变更流水,按版本列取最终状态
  • 替代「先删后插」的更新方式,避免 DELETE 引发的大范围分区重写
  • 作为物化视图的目标表,配合聚合函数实现按主键取最新

建表与读取

建表与读取
CREATE TABLE users
(
    id UInt64,
    name String,
    updated_at DateTime
)
ENGINE = ReplacingMergeTree(updated_at)   -- 以 updated_at 为版本列,值越大越新
ORDER BY id;                              -- 排序键就是去重键

INSERT INTO users VALUES (1, 'alice', now() - 100), (1, 'alice-new', now());
INSERT INTO users VALUES (1, 'alice-older', now() - 200);  -- 版本更旧,合并后会被替换

SELECT * FROM users FINAL WHERE id = 1;    -- 查询时完成去重
SELECT id, argMax(name, updated_at) AS name FROM users GROUP BY id;  -- 手工取最新的等价写法

去重行为读法

场景行为注意
同分区且排序键相同合并后只保留版本最大的一行合并时机由后台调度决定,可能长时间不触发
跨分区同排序键不去重,两行都会保留按天分区时同一主键跨天变更会留下多行
版本列取值相同保留哪一行不确定版本列必须严格递增,精度不够会撞车
查询不带 FINAL可能同时看到新旧多行计数与求和口径会偏大
该引擎的去重是最终一致而非即时生效:刚写入的新旧两行在合并前都会出现在查询结果中,所以业务侧要么查询时带 FINAL,要么用 GROUP BY 配合 argMax 兜底,不能假设重复行已经消失。

易错点

  • 以为写入即去重、查询又不带 FINAL,行数统计与数值求和都会翻倍
  • 版本列精度不足(例如用秒级时间戳),同一秒内多次变更版本相同,最终保留哪一行不可预期
  • 分区键选得过细(如按小时分区),同一主键的变更落进不同分区,去重永远不会发生
  • 用频繁 OPTIMIZE 换取即时去重,代价是反复重写整个分区,写入吞吐与磁盘压力都会明显上升

数据来源与参考

  • ClickHouse Docs: ReplacingMergeTree
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌