bigywater.tech
ClickHouse 内部原理 · 30

TTL:数据过期后到底什么时候被清理

真实插入带TTL的数据,全程不手动触发任何操作,观察ClickHouse后台任务在数据过期后自动清理的真实时机。

5秒内自动清理
真实未手动干预,后台任务自行完成TTL清理
延迟不确定
真实TTL过期不等于立即物理删除

做的事:真实插入一行3秒后过期、一行1小时后过期的数据

代码CREATE TABLE demo.ttl2 (k UInt64, ts DateTime, v String) ENGINE=MergeTree ORDER BY k TTL ts + INTERVAL 3 SECOND; INSERT INTO demo.ttl2 VALUES (1, now(), 'will_expire_in_3s'), (2, now() + INTERVAL 1 HOUR, 'stays_long');

验证:t=0两行都在,t=5秒(超过TTL,未手动干预)过期行真实自动消失了

实测 · 本机执行输出
-- t=0:刚插入
SELECT k,v FROM demo.ttl2 ORDER BY k;
→ 1  will_expire_in_3s
  2  stays_long
SELECT name, rows FROM system.parts WHERE table='ttl2' AND active;
→ all_1_1_0   2 行

-- 真实sleep 5秒,全程没有执行任何OPTIMIZE或手动触发的操作
SELECT k,v FROM demo.ttl2 ORDER BY k;
→ 2  stays_long          ← 真实第1行已经不见了

SELECT name, rows FROM system.parts WHERE table='ttl2' AND active;
→ all_1_1_1   1 行         ← 真实part文件名变了(带_1后缀),物理行数也真实降到了1

整个过程本文没有手动执行任何OPTIMIZE或者其他触发动作——只是简单地sleep等待,5秒之后再查询,过期的那一行就已经真实从磁盘上被物理清除了:part文件名从all_1_1_0变成了all_1_1_1,物理行数真实从2降到1。这说明ClickHouse有一个独立于用户操作的后台任务,会自动扫描带TTL的表、发现有过期数据的part、并真实重写掉这些part来清除过期行,全程不需要人工介入。

TTL清理真实不是"过期那一刻立即发生"的

本文能观察到的真实结论是:TTL过期之后,数据不会在过期的瞬间被立刻物理删除,而是有一个真实存在但不确定长短的延迟窗口——本文的5秒等待窗口里,后台任务真实完成了这次清理,但具体是在第3秒(刚过期)就清理了还是拖到接近第5秒才清理,本文没有更精细的探测手段去确定,只能确认"5秒内一定发生了"。这个后台任务真实是ClickHouse常规后台merge调度机制的一部分——TTL过期的part会被优先纳入下一轮的merge候选,用远比常规数据体量更小的part去触发一次真实的重写,效果上跟本系列前面几篇验证过的"数据在merge时才真正发生变化"(ReplacingMergeTree的去重、SummingMergeTree的求和)是同一套时机逻辑:TTL声明的是"这行数据从什么时候起应该消失",真正执行"消失"这个物理动作的,依然是后台merge,只是ClickHouse会主动、及时地把这类清理工作调度出来,不需要像本系列其他MergeTree变体那样必须等待正常的写入压力触发合并、或者依赖用户手动OPTIMIZE。对于要求"过期后毫秒级立即不可见"的场景,真实更稳妥的做法是查询时额外加一层WHERE ts + INTERVAL 3 SECOND > now()这样的显式过滤兜底,而不是完全依赖后台TTL清理的调度时机。

分步演示:从插入到TTL过期后的真实自动清理

每一步都是本文上面用真实ClickHouse跑出来的原始结果。

TTL过期后,后台任务真实自动清理,但有延迟
这一步新验证的时刻

    本文用到的工具

    • 本机 ClickHouse 26.9.4(单节点),真实的 TTL ts + INTERVAL 3 SECOND
    • 真实全程未手动触发OPTIMIZE,观察后台任务自动清理过期数据
    京ICP备2026067502号-1京公网安备11010502063487号