78.【数据库】ClickHouse从入门到放弃-MergeTree 数据TTL

406 阅读6分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 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:002019-05-13 17:00:008640086400      │
└─────────────┴─────────────┴────────┴────────┘

则能够印证,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数据表启停,但聊胜于无吧。