📅 2025年3月20日

📦 ck版本 25.1.3.23

🏡 MergeTree之TTL和存储策略

1️⃣ MergeTree

之前分为几个阶段了解过 MergeTree了,这里主要讲TTL和存储策略

🌟 TTL

MergeTree中可以设置、表、列的 TTL,如果同时设置了则会以哪个先到期为先,但是无论是列级别还是表级别的 TTL,都需要依托某个 DateTimeDate类型的字段,通过对这个时间字段的 INTERVAL操作,来表述 TTL的过期时间,比如如下:

-- 表示在这两个类型字段时间内的 3 天后过期
TTL [DateTime|Date类型字段] + INTERVAL 3 DAY

INTERVAL完整的操作包括 SECONDMINUTEHOURDAYWEEKMONTHQUARTERYEAR

🌊 列级别TTL

如果想要设置列级别的

TTL,则需要在定义表字段的时候,为它们声明 TTL表达式,主键字段不能被声明 TTL

如下语句中code和type会在create_time 时间基础上+10秒后被删除
CREATE TABLE ttl_table_v1(  
    id String,  
    create_time DateTime,  
    code String TTL create_time + INTERVAL 10 SECOND,  
    type UInt8 TTL create_time + INTERVAL 10 SECOND
)  ENGINE = MergeTree  PARTITION BY toYYYYMM(create_time)  ORDER BY id

然后测试一下,在插入数据10秒后再次查看次表会发现数据会被归为零值

-- 插入数据
 INSERT INTO TABLE ttl_table_v1 VALUES('A000',now(),'C1',1),  ('A000',now() + INTERVAL 10 MINUTE,'C1',1);
 
 -- 查看数据
 SELECT *
FROM ttl_table_v1

Query id: 991b269b-5513-4a94-876f-e2983fadf68d

   ┌─id───┬─────────create_time─┬─code─┬─type─┐
1. │ A000 │ 2025-03-20 22:58:59 │ C1   │    1 │
2. │ A000 │ 2025-03-20 23:08:59 │ C1   │    1 │
   └──────┴─────────────────────┴──────┴──────┘
   
-- 数10秒后使用optimize触发合并
optimize TABLE ttl_table_v1 FINAL

-- 再次查看
SELECT *
FROM ttl_table_v1

Query id: a785b458-a0a6-4da0-be7c-342ff9970361

   ┌─id───┬─────────create_time─┬─code─┬─type─┐
1. │ A000 │ 2025-03-20 22:58:59 │      │    0 │
2. │ A000 │ 2025-03-20 23:08:59 │ C1   │    1 │
   └──────┴─────────────────────┴──────┴──────┘

如果想要修改列字段的TTL,或是为已有字段添加TTL,则可以使用ALTER语句

ALTER TABLE ttl_table_v1 MODIFY COLUMN code String TTL create_time + INTERVAL 1  DAY

目前ck没有删除列表级别ttl方法

🌊 表级别TTL

表级别和列级别差不多,这里就只举列子如何定义和修改语句

CREATE TABLE ttl_table_v2(  
    id String, 
    create_time DateTime,
    code String TTL create_time + INTERVAL 1 MINUTE, 
    type UInt8  
)
ENGINE = MergeTree  PARTITION BY toYYYYMM(create_time)  ORDER BY create_time  
TTL create_time + INTERVAL 1 DAY



-- 修改
ALTER TABLE ttl_table_v2 MODIFY TTL create_time + INTERVAL 3 DAY

🖌 原理

如果一张

MergeTree表被设置了 TTL表达式,那么在写入数据时,会以数据分区为单位,在每个分区目录内生成一个名为 ttl.txt的文件如下

/var/lib/clickhouse/data/default/ttl_table_v1/202503_1_1_3# ls
checksums.txt  data.bin                       metadata_version.txt    primary.cidx
columns.txt    data.cmrk3                     minmax_create_time.idx  serialization.json
count.txt      default_compression_codec.txt  partition.dat           ttl.txt
##查看ttl文件
root@DESKTOP-HLBQNO4:/var/lib/clickhouse/data/default/ttl_table_v1/202503_1_1_3# cat ttl.txt
ttl format version: 1
{"columns":[{"name":"code","min":1742483349,"max":1742483349,"finished":0},{"name":"type","min":1742483349,"max":1742483349,"finished":0}]}
root@DESKTOP-HLBQNO4:/var/lib/clickhouse/data/default/ttl_table_v1/202503_1_1_3#

MergeTree是通过一串 JSON配置保存了TTL的相关信息,其中:

  • columns用于保存列级别TTL信息;
  • table用于保存表级别TTL信息;
  • min和max则保存了当前数据分区内,TTL指定日期字段的最小值、最大值分别与INTERVAL表达式计算后的时间戳

TTL的大致处理逻辑

  1. MergeTree以分区目录为单位,通过 ttl.txt文件记录过期时间,并将其作为后续的判断依据
  2. 每当写入一批数据时,都会基于 INTERVAL表达式的计算结果为这个分区生成 ttl.txt文件
  3. 只有在 MergeTree合并分区时,才会触发删除TTL过期数据的逻辑
  4. 在选择删除的分区时,会使用贪婪算法,它的算法规则是尽可能找到会最早过期的,同时年纪又是最老的分区(合并次数更 多,MaxBlockNum更大的)
  5. 如果一个分区内某一列数据因为TTL到期全部被删除了,那么在合并之后生成的新分区目录中,将不会包含这个列字段的数据文件(.bin和.mrk)

(1)TTL默认的合并频率由 MergeTreemerge_with_ttl_timeout参数控制,默认86400秒,即1天。它维 护的是一个专有的TTL任务队列。有别于MergeTree的常规合并任务,如果这个值被设置的过小,可能会带来性能损耗。

(2)除了被动触发TTL合并外,也可以使用optimize命令强制触发合并。例如,触发一个分区合并

(3)最新版本加入了取消TTL了: https://clickhouse.com/docs/sql-reference/statements/alter/ttl#remove-ttl