开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第14天,点击查看活动详情 |
文档参考:《ClickHouse原理解析与应用实践(数据库技术丛书)(朱凯)》
70.【数据库】ClickHouse从入门到放弃-MergeTree的创建方式 - 掘金 (juejin.cn)
71.【数据库】ClickHouse从入门到放弃-MergeTree的存储结构 - 掘金 (juejin.cn)
72.【数据库】ClickHouse从入门到放弃-数据分区 - 掘金 (juejin.cn)
73.【数据库】ClickHouse从入门到放弃-一级索引 - 掘金 (juejin.cn)
74.【数据库】ClickHouse从入门到放弃-二级索引 - 掘金 (juejin.cn)
75.【数据库】ClickHouse从入门到放弃-数据存储 - 掘金 (juejin.cn)
76.【数据库】ClickHouse从入门到放弃-数据标记 - 掘金 (juejin.cn)
77.【数据库】ClickHouse从入门到放弃-对于分区、索引、标记和压缩数据的协同总结 - 掘金 (juejin.cn)
MergeTree作为家族系列最基础的表引擎,提供了数据分区、一级索引和二级索引等功能。对于它们的运行机理,前面已经进行了详细介绍。今天进一步学习MergeTree家族独有的另外两项能力——数据TTL与存储策略。
1 数据TTL
TTL即Time To Live,顾名思义,它表示数据的存活时间。在MergeTree中,可以为某个列字段或整张表设置TTL。当时间到达时,如果是列字段级别的TTL,则会删除这一列的数据;如果是表级别的TTL,则会删除整张表的数据;如果同时设置了列级别和表级别的TTL,则会以先到期的那个为主。
无论是列级别还是表级别的TTL,都需要依托某个DateTime或Date类型的字段,通过对这个时间字段的INTERVAL操作,来表述TTL的过期时间,例如:
TTL time_col + INTERVAL 3 DAY
上述语句表示数据的存活时间是time_col时间的3天之后。又例如:
TTL time_col + INTERVAL 1 MONTH
上述语句表示数据的存活时间是time_col时间的1月之后。INTERVAL完整的操作包括SECOND、MINUTE、HOUR、DAY、WEEK、MONTH、QUARTER和YEAR。
1.1.列级别TTL
如果想要设置列级别的TTL,则需要在定义表字段的时候,为它们声明TTL表达式,主键字段不能被声明TTL。以下面的语句为例:
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
其中,create_time是日期类型,列字段code与type均被设置了TTL,它们的存活时间是在create_time的取值基础之上向后延续10秒。
现在写入测试数据,其中第一行数据create_time取当前的系统时间,而第二行数据的时间比第一行增加10分钟:
INSERT INTO TABLE ttl_table_v1 VALUES('A000',now(),'C1',1),
('A000',now() + INTERVAL 10 MINUTE,'C1',1)
SELECT * FROM ttl_table_v1
┌─id───┬─────create_time──┬─code─┬─type─┐
│ A000 │ 2019-06-12 22:49:00 │ C1 │ 1 │
│ A000 │ 2019-06-12 22:59:00 │ C1 │ 1 │
└────┴───────────────┴────┴─────┘
接着心中默数10秒,然后执行optimize命令强制触发TTL清理:
optimize TABLE ttl_table_v1 FINAL
再次查询ttl_table_v1则能够看到,由于第一行数据满足TTL过期条件(当前系统时间>=create_time+10秒),它们的code和type列会被还原为数据类型的默认值:
┌─id───┬───────create_time─┬─code─┬─type─┐
│ A000 │ 2019-06-12 22:49:00 │ │ 0 │
│ A000 │ 2019-06-12 22:59:00 │ C1 │ 1 │
└─────┴───────────────┴─────┴─────┘
如果想要修改列字段的TTL,或是为已有字段添加TTL,则可以使用ALTER语句,示例如下:
ALTER TABLE ttl_table_v1 MODIFY COLUMN code String TTL create_time + INTERVAL 1 DAY
目前ClickHouse没有提供取消列级别TTL的方法。
1.2.表级别TTL
如果想要为整张数据表设置TTL,需要在MergeTree的表参数中增加TTL表达式,例如下面的语句:
code String TTL create_time + INTERVAL 1 MINUTE
同时被设置了表级别的TTL:
TTL create_time + INTERVAL 1 DAY
那么,在写入数据之后,它的每个分区目录内都会生成ttl.txt文件:
# pwd
/chbase/data/data/default/ttl_table_v2/201905_1_1_0
# ll
total 60
…省略
-rw-r-----. 1 clickhouse clickhouse 38 May 13 14:30 create_time.bin
-rw-r-----. 1 clickhouse clickhouse 48 May 13 14:30 create_time.mrk2
-rw-r-----. 1 clickhouse clickhouse 8 May 13 14:30 primary.idx
-rw-r-----. 1 clickhouse clickhouse 67 May 13 14:30 ttl.txt
…省略
进一步查看ttl.txt的内容:
cat ./ttl.txt
ttl format version: 1
{"columns":[{"name":"code","min":1557478860,"max":1557651660}],"table":{"min":1557565200,"max":1557738000}}
通过上述操作会发现,原来MergeTree是通过一串JSON配置保存了TTL的相关信息,其中:
·columns用于保存列级别TTL信息;
·table用于保存表级别TTL信息;
·min和max则保存了当前数据分区内,TTL指定日期字段的最小值、最大值分别与INTERVAL表达式计算后的时间戳。
如果将table属性中的min和max时间戳格式化,并分别与create_time最小与最大取值对比:
SELECT
toDateTime('1557565200') AS ttl_min,
toDateTime('1557738000') AS ttl_max,
ttl_min - MIN(create_time) AS expire_min,
ttl_max - MAX(create_time) AS expire_max
FROM ttl_table_v2
┌─────ttl_min────┬────ttl_max────┬─expire_min┬─expire_max─┐
│ 2019-05-11 17:00:00 │ 2019-05-13 17:00:00 │ 86400 │ 86400 │
└─────────────┴─────────────┴────────┴────────┘
则能够印证,ttl.txt中记录的极值区间恰好等于当前数据分区内create_time最小与最大值增加1天(1天=86400秒)所表示的区间,与TTL表达式create_time+INTERVAL 1 DAY的预期相符。
在知道了TTL信息的记录方式之后,现在看看它的大致处理逻辑。
(1)MergeTree以分区目录为单位,通过ttl.txt文件记录过期时间,并将其作为后续的判断依据。
(2)每当写入一批数据时,都会基于INTERVAL表达式的计算结果为这个分区生成ttl.txt文件。
(3)只有在MergeTree合并分区时,才会触发删除TTL过期数据的逻辑。
(4)在选择删除的分区时,会使用贪婪算法,它的算法规则是尽可能找到会最早过期的,同时年纪又是最老的分区(合并次数更多,MaxBlockNum更大的)。
(5)如果一个分区内某一列数据因为TTL到期全部被删除了,那么在合并之后生成的新分区目录中,将不会包含这个列字段的数据文件(.bin和.mrk)。
这里还有几条TTL使用的小贴士。
(1)TTL默认的合并频率由MergeTree的merge_with_ttl_timeout参数控制,默认86400秒,即1天。它维护的是一个专有的TTL任务队列。有别于MergeTree的常规合并任务,如果这个值被设置的过小,可能会带来性能损耗。
(2)除了被动触发TTL合并外,也可以使用optimize命令强制触发合并。例如,触发一个分区合并:
optimize TABLE table_name
触发所有分区合并:
optimize TABLE table_name FINAL
(3)ClickHouse目前虽然没有提供删除TTL声明的方法,但是提供了控制全局TTL合并任务的启停方法:
SYSTEM STOP/START TTL MERGES
虽然还不能做到按每张MergeTree数据表启停,但聊胜于无吧。