1 数据 TTL
TTL(Time To Live)表示数据的存活时间,在 Merge 中可以为某个字段或者整个表设置TTL。
如果设置列级别的 TTL,那么到期时会删除这一列的数据
如果设置表级别的 TTL,那么到期时会删除整个表的数据
如果同时设置了列级别和表级别的 TTL,那么会以先到期的为主。
无论是列级别还是表级别的 TTL 都要依靠 DateTime 或 Date 类型的字段,通过对这个字段的 Interval 操作,来表述 TTL 的过期时间。
INTERVAL 的完整操作包括:SECOND、MINUTE、HOUR、DAY、WEEK、MONTH、QUARTER 和 YEAR
1 2 3 4 5 TTL create_time + INTERVAL 3 DAY TTL create_time + INTERVAL 3 MONTH
2. 列级别 TTL
设置列级别的 TTL,需要在建表时设置列的 TTL 表达式
主键不能被设置 TTL
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 CREATE TABLE merge_column_ttl( `id` String, `create_time` DateTime, `code1` String TTL create_time + INTERVAL 10 SECOND , `code2` UInt8 TTL create_time + INTERVAL 15 SECOND ) ENGINE = MergeTree PARTITION BY toYYYYMM(create_time)ORDER BY id;describe merge_column_ttl;┌─name────────┬─type─────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─────────────────────┐ │ id │ String │ │ │ │ │ │ │ create_time │ DateTime │ │ │ │ │ │ │ code1 │ String │ │ │ │ │ create_time + toIntervalSecond(10 ) │ │ code2 │ Int32 │ │ │ │ │ create_time + toIntervalSecond(15 ) │ └─────────────┴──────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────────────────────────┘ insert into table merge_column_ttl values ('A01' ,now(),'A01-code1' ,'100' ), ('A02' ,now() + INTERVAL 3 MINUTE ,'A02-code1' ,'99' ); select * from merge_column_ttl;┌─id──┬─────────create_time─┬─code1─────┬─code2─┐ │ A01 │ 2021 -06 -23 15 :50 :17 │ │ 100 │ │ A02 │ 2021 -06 -23 15 :53 :17 │ A02- code1 │ 99 │ └─────┴─────────────────────┴───────────┴───────┘ optimize table merge_column_ttl final ; select * from merge_column_ttl;┌─id──┬─────────create_time─┬─code1─────┬─code2─┐ │ A01 │ 2021 -06 -23 15 :50 :17 │ │ 0 │ │ A02 │ 2021 -06 -23 15 :53 :17 │ A02- code1 │ 99 │ └─────┴─────────────────────┴───────────┴───────┘
1 2 3 4 5 6 7 8 9 10 11 12 13 alter table merge_column_ttl modify column code1 String TTL create_time + INTERVAL 10 MINUTE ;describe merge_column_ttl;┌─name────────┬─type─────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─────────────────────┐ │ id │ String │ │ │ │ │ │ │ create_time │ DateTime │ │ │ │ │ │ │ code1 │ String │ │ │ │ │ create_time + toIntervalMinute(10 ) │ │ code2 │ Int32 │ │ │ │ │ create_time + toIntervalSecond(15 ) │ └─────────────┴──────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────────────────────────┘
3. 表级别 TTL
想要为整张表设置 TTL,需要在建表时设置表的 TTL 表达式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 CREATE TABLE merge_table_ttl( `id` String, `create_time` DateTime, `code1` String TTL create_time + INTERVAL 10 SECOND , `code2` UInt8 ) ENGINE = MergeTree PARTITION BY toYYYYMM(create_time)ORDER BY idTTL create_time + INTERVAL 15 SECOND ; insert into table merge_table_ttl values ('A01' ,now(),'A01-code1' ,'100' ), ('A02' ,now() + INTERVAL 3 MINUTE ,'A02-code1' ,'99' ); select * from merge_table_ttl;┌─id──┬─────────create_time─┬─code1─────┬─code2─┐ │ A01 │ 2021 -06 -23 16 :26 :12 │ A01- code1 │ 100 │ │ A02 │ 2021 -06 -23 16 :29 :12 │ A02- code1 │ 99 │ └─────┴─────────────────────┴───────────┴───────┘ optimize table merge_table_ttl final ; select * from merge_table_ttl;┌─id──┬─────────create_time─┬─code1─────┬─code2─┐ │ A01 │ 2021 -06 -23 16 :26 :12 │ │ 100 │ │ A02 │ 2021 -06 -23 16 :29 :12 │ A02- code1 │ 99 │ └─────┴─────────────────────┴───────────┴───────┘ optimize table merge_table_ttl final ; select * from merge_table_ttl;┌─id──┬─────────create_time─┬─code1─────┬─code2─┐ │ A02 │ 2021 -06 -23 16 :29 :12 │ A02- code1 │ 99 │ └─────┴─────────────────────┴───────────┴───────┘
1 alter table merge_table_ttl modify ttl create_time + INTERVAL 15 MINUTE ;
4. TTL 运行原理 如果一张表被设置了 TTL,在写入数据时,会以数据分区为单位,在每个分区目录中生成一个 ttl.txt 的文件,如 merge_table_ttl 表即被设置了列级别的 TTL 也被设置了表级别的 TTL,那么每个分区目录都会生成 ttl.txt 文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 cd /var/lib/clickhouse/data/db_merge/merge_table_ttl/202106_1_1_3 [root@node3 202106_1_1_3]# cat ttl.txt ttl format version: 1 {"columns":[{"name":"code1","min":1624436962,"max":1624436962}],"table":{"min":1624436967,"max":1624436967}} # 将上面的 json 格式化 并加上描述 # MergeTree 通过 json 结构的数据保存 ttl 的相关信息: { "columns": [ # 用于保存 列级别的 TTL 信息 { "name": "code1", "min": 1624436962, "max": 1624436962 } ], "table": {# 用于保存 表级别的 TTL 信息 "min": 1624436967, "max": 1624436967 } }
min 与 max 保存了当前分区内,TTL 指定日期字段的最大值,最小值分别与 INTERVAL 表达式计算后的时间戳。
如果将table 属性中的min 和 max 时间戳格式化,并分别与 create_time 的最大,最小取值作对比。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 SELECT toDateTime('1624436967' ) AS ttl_min, toDateTime('1624436967' ) AS ttl_max, ttl_min - MIN (create_time) AS expire_min, ttl_max - MAX (create_time) AS expire_max FROM merge_table_ttlQuery id: ad5dc6af-1 f8c-41 a2-829 a-802 ad702cfbe ┌─────────────ttl_min─┬─────────────ttl_max─┬─expire_min─┬─expire_max─┐ │ 2021 -06 -23 16 :29 :27 │ 2021 -06 -23 16 :29 :27 │ 15 │ 15 │ └─────────────────────┴─────────────────────┴────────────┴────────────┘
ttl.txt记录的 min,max 值恰好对应 create_time + INTERVAL 15 SECOND 的值。
MergeTree 处理 TTL 的大致逻辑如下:
MergeTree 以分区目录为单位,通过 ttl.txt 文件记录过期时间,并将其作为后续判断依据
每当写入一批数据时,都会基于 INTERVAL 表达式的计算结果为这个分区生成 ttl.txt文件
只有在 MergeTree 分区合并时,才会触发删除过期数据的逻辑。
在删除数据时,会使用贪婪算法,算法规则是尽可能找到会最早过期的,同时年纪又最老的分区(合并次数最多,MaxBlockNum最大)
如果一个分区的某个字段因为 TTL 到期,导致数据被全部删除,那么合并分区时在生成新分区将不会创建该字段的数据文件(.mrk、.bin)
TTL 的默认合并频率由MergeTree 的 merge_with_ttl_timeout 参数控制,默认 86400 秒,即 1 天,它维护一个专门的 TTL 队列。如果这个时间设置的过小会带来性能损耗。
除了被动触发 TTL,还可以使用optimize 命令强制触发合并。
1 2 3 4 5 6 7 optimize table table_name optimize table table_name final
Clickhouse 提供了控制全局 TTL 任务合并的方法,但是不能按照数据表停起
1 2 3 SYSTEM STOP/ START MERGES