首页/数据/NoSQL 速查/FINAL 去重读取
ClickHouse56 条

FINAL 去重读取

FINAL 在查询时按排序键对 ReplacingMergeTree 等表做去重/折叠,得到合并完成后的逻辑结果;代价是读取放大,适合小范围查询或对账,长跑查询要评估开销。

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

FINAL 做什么

FINAL 是一个查询子句,读取时按排序键对 ReplacingMergeTree、SummingMergeTree、CollapsingMergeTree 这类表做去重或折叠,返回合并完成后应有的逻辑结果。它的代价是读取放大,适合范围受限的查询与对账,全表 FINAL 在宽表上可能慢一个量级。

基本信息

写法SELECT … FROM t FINAL WHERE …
作用范围ReplacingMergeTree、CollapsingMergeTree、SummingMergeTree 等表家族
语义保证结果与后台是否已经合并无关,未合并的重复行由查询时处理
主要代价读取放大,需要先按排序键归并同一批数据
替代方案对并发要求高时改用 GROUP BY 加 argMax,或读预聚合表

典型使用场景

  • 按主键取最新状态:读 ReplacingMergeTree 表的当前值
  • 报表对账:需要与业务口径一致的去重结果
  • 排查数据:确认某条主键的最终版本是否符合预期
  • 小范围点查:条件已限定到少量主键时开销可控
  • 作为临时校验手段,与 argMax 写法交叉验证结果

常用写法

常用写法
-- 取每个 id 的最新一行(ReplacingMergeTree 语义)
SELECT id, name, ts
FROM users FINAL
WHERE id IN (1, 2, 3);

-- 等价的手写去重写法,聚合场景更省资源
SELECT id, argMax(name, ts) AS name, max(ts) AS ts
FROM users
WHERE id IN (1, 2, 3)
GROUP BY id;

-- 先限定分区与时间范围,显著降低 FINAL 开销
SELECT id, name FROM users FINAL
WHERE ts >= today() - 7 AND id IN (1, 2, 3);

-- 交叉验证两种写法的返回行数是否一致
SELECT count() FROM users FINAL WHERE id IN (1, 2, 3);

读法对照

方式适用代价
FINAL 全量查询小范围点查与对账读取放大,宽表上尤其明显
FINAL 加分区过滤范围受限的业务查询先裁剪再去重,开销可控
GROUP BY 加 argMax聚合类统计自己写聚合逻辑,结果等价
预聚合物化视图高频读取场景需要额外存储与维护成本
FINAL 的行为与后台合并进度无关:未合并的重复行由查询时处理,结果始终符合语义,只是更慢;想加快只能缩小查询范围或换用其他读法。
FINAL 只按排序键去重;业务唯一键与排序键不一致时,同一业务键仍可能返回多行,这种情况下要先调整表设计,而不是反复调整查询条件。

易错点

  • 大表全量 FINAL 查询可能比普通查询慢一个量级,先把条件限定到分区或时间范围再使用
  • FINAL 只保证按排序键去重,业务唯一键与排序键不一致时结果仍会重复,需要先调整表设计
  • 以为 FINAL 会触发落盘整理:它只改变本次查询的读取方式,磁盘上的重复行依然存在
  • 在高并发接口里给所有查询默认加 FINAL,读取放大叠加并发后节点负载明显上升,应改为预聚合表或写入侧去重

数据来源与参考

  • ClickHouse Docs: FINAL clause
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌