首页/数据/NoSQL 速查/OPTIMIZE 触发合并与去重
ClickHouse56 条

OPTIMIZE 触发合并与去重

OPTIMIZE TABLE 强制触发 part 合并;PARTITION 限定范围,FINAL 合并到单 part,DEDUPLICATE 按排序键或指定列去重(ReplacingMergeTree)。

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

OPTIMIZE 是干什么的

OPTIMIZE TABLE 手动触发 part 合并:PARTITION 限定处理范围,FINAL 把分区内的多个 part 合并成一个,DEDUPLICATE 在支持去重的引擎上按排序键或指定列去重。它常用于批量导入之后、对账之前,或者需要立刻回收重复数据的时刻。

为什么不能滥用

合并本质是重写数据:对大分区做 FINAL 会重写该分区的全部数据,占用大量 IO 与临时磁盘空间。而且 OPTIMIZE 默认异步提交,语句返回成功只代表任务已入队,不代表合并完成。日常写入的表不需要手工合并,后台合并会自动进行,频繁手工触发只会和后台任务互相抢资源。

基本信息

常用形式OPTIMIZE TABLE t [PARTITION p] [FINAL] [DEDUPLICATE [BY cols]]
执行方式默认异步排队,不等待完成
等待完成设置 mutations_sync = 1 或 2
适用时机批量导入后、对账前、需要立即去重时
去重前提需要 ReplacingMergeTree 一类支持去重的引擎

示例

示例
-- 只合并指定分区
OPTIMIZE TABLE events PARTITION '202401' FINAL;

-- 等待合并完成(按副本等待)
OPTIMIZE TABLE events FINAL SETTINGS mutations_sync = 2;

-- 指定去重键
OPTIMIZE TABLE users DEDUPLICATE BY id, ts;

-- 观察 part 数量变化,确认合并是否真的完成
SELECT count() AS parts, sum(rows) AS rows
FROM system.parts
WHERE table = 'events' AND active;

典型使用场景

  • 批量导入之后:把小 part 合并,改善后续查询效率
  • 对账之前:先合并让查询结果稳定,避免读到重复键
  • 去重取最新:按业务键与版本列去重
  • 分区维护:对历史分区做一次性整理
  • 排查 part 过多:确认是否需要一次性合并降低元数据开销

子句作用对照

子句作用注意
PARTITION p只处理指定分区不写则处理整表
FINAL把分区合并到单个 part数据量大时 IO 与磁盘占用很高
DEDUPLICATE按排序键去重只对支持去重的引擎有效
BY c1, c2指定去重列列需要能参与比较与排序
mutations_sync等待合并完成设为 2 时按副本等待,耗时更长
判断合并是否真的完成,要看 system.parts 里该分区 active part 的数量变化,而不是只看语句的返回值。
对大表执行 FINAL 会重写全部分区数据,必须在低峰执行并预留足够临时磁盘空间,空间不足时任务会失败并影响同时进行的写入。

易错点

  • 对大表执行 FINAL 会重写全部数据,务必在低峰执行并预留磁盘空间
  • OPTIMIZE 返回成功只代表任务已提交,不代表合并完成,需要时用 mutations_sync 或观察 system.parts
  • 把去重当成永久生效的约束:后续写入仍会带来新的重复键,查询侧要按引擎语义处理
  • 频繁手工合并会与后台合并任务争抢资源,日常写入的表不必定期 OPTIMIZE

数据来源与参考

  • ClickHouse Docs: OPTIMIZE statement
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌