一、数据TTL TTL(Time to Live),表示数据的存活时间。在MergeTree中,可以为某个列字段或整张表设置TTL时间。若为列字段的TTL,当时间到期时,则会删除这一列的数据;若为表级别的TTL,当时间到期时,则会删除整张表的数据;若一张表同时设置了列级别和表级别的TTL,则会以先到期的为主。
无论是列级别还是表级别的TTL,都需要依托于某个DateTime或Date类型的字段,通过对这个时间字段的INTERVAL操作,来描述TTL的过期时间,设置TTL过期时间的INTERVAL完整操作包括:SECOND、MINUTE、HOUR、DAY、WEEK、MONTH、QUARTER和YEAR。例如:
1 2 3 4 5 TTL time_col + INTERVAL 3 DAY TTL time_col + INTERVAL 1 MONTH
二、列级别TTL 若设置了列级别的TTL,当列字段中的值过期时,ClickHouse会将他们替换成默认值。如果一个分区内,某一列的所有值都已过期,那么ClickHouse会从文件系统中删除这个分区目录下的列文件。
如果想要设置列级别的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 CREATE TABLE ttl_table_t1( `id` String , `create_time` DateTime, `code` String TTL create_time + toIntervalSecond(10 ), `type` UInt8 TTL create_time + toIntervalSecond(10 ) ) ENGINE = MergeTreePARTITION BY toYYYYMM(create_time)ORDER BY id INSERT INTO ttl_table_t1 VALUES ('A000' , now (), 'c1' , 1 ), ('A000' , now () + INTERVAL 2 MINUTE , 'c1' , 2 );select * from ttl_table_t1;┌─id───┬─────────create_time─┬─code─┬─type─┐ │ A000 │ 2020-07-27 21:29:15 │ c1 │ 1 │ │ A000 │ 2020-07-27 21:31:15 │ c1 │ 2 │ └──────┴─────────────────────┴──────┴──────┘
ClickHouse看到数据已经过期的时候,将执行合并,合并的频率由merge_with_ttl_timeout
参数控制,SETTINGS merge_with_ttl_timeout = 86400
默认为86400s即1天,如果这个值太低,表示ClickHouse需要执行许多计划外的合并,可能消耗大量的资源。
如果在ClickHouse合并期间进行查询,可能会获得过期的数据,所以在select查询之前可使用optimize
命令强制触发TTL清理机制。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 select * from system.merge_tree_settings where name ='merge_with_ttl_timeout' ;┌─name───────────────────┬─value─┬─changed─┬─description───────────────────────────────────────────────────┐ │ merge_with_ttl_timeout │ 86400 │ 0 │ Minimal time in seconds, when merge with TTL can be repeated. │ └────────────────────────┴───────┴─────────┴───────────────────────────────────────────────────────────────┘ optimize TABLE ttl_table_t1 FINAL ;select * from ttl_table_t1;┌─id───┬─────────create_time─┬─code─┬─type─┐ │ A000 │ 2020-07-27 21:29:15 │ │ 0 │ │ A000 │ 2020-07-27 21:31:15 │ c1 │ 2 │ └──────┴─────────────────────┴──────┴──────┘
如果需要修改列字段的TTL或为已有字段添加TTL,可使用ALTER语句设置:ALTER TABLE ttl_table_t1 MODIFY COLUMN code STRING TTL create_time + INTERVAL 1 DAY
,目前ClickHouse没有提供取消列级别TTL的方法。
三、表级别TTL 如果需要为整张表设置TTL过期时间,则需要在MergeTree的表参数重增加TTL表达式,例如:
1 2 3 4 5 6 7 8 9 10 11 CREATE TABLE ttl_table_t2( `id` String , `create_time` DateTime, `code` String TTL create_time + toIntervalSecond(10 ), `type` UInt8 TTL create_time + toIntervalSecond(10 ) ) ENGINE = MergeTreePARTITION BY toYYYYMM(create_time)ORDER BY id TTL create_time + INTERVAL 1 DAY
上面示例中整张表都被设置了TTL,当触发到TTL清理时,满足过期时间的数据行将被整行删除。表级别的TTL也支持修改,同样适用ALTER语句:ALTER TABLE ttl_table_t2 MODIFY TTL create_time + INTERVAL 3 DAY
,同样,表级别的TTL目前也不支持删除。
1 2 3 4 5 6 7 8 9 10 11 clickhouse-server_1 :) desc ttl_table_t2 DESCRIBE TABLE ttl_table_t2┌─name ────────┬─type ─────┬─default_type─┬─default_expression─┬─comment ─┬─codec_expression─┬─ttl_expression─────────────────────┐ │ id │ String │ │ │ │ │ │ │ create_time │ DateTime │ │ │ │ │ │ │ code │ String │ │ │ │ │ create_time + toIntervalSecond(10 ) │ │ type │ UInt8 │ │ │ │ │ create_time + toIntervalSecond(10 ) │ └─────────────┴──────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────────────────────────┘
四、TTL的运行原理 当一张MergeTree表被设置了TTL表达式,在写入数据时,会以数据分区为单位,在每个分区目录内生成一个ttl.txt
的文件,以上面的表ttl_table_t2为例,它既包含了列级别TTL,还包含了表级别的TTL。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 insert into ttl_table_t2 VALUES ('A000' , now (), 'haha' , 2 ), ('A001' , now (), 'xx' , 2 ) ;select * from ttl_table_t2┌─id ───┬─────────create_time─┬─code─┬─type ─┐ │ A000 │ 2020 -07 -28 14 :28 :23 │ haha │ 2 │ │ A001 │ 2020 -07 -28 14 :28 :23 │ xx │ 2 │ └──────┴─────────────────────┴──────┴──────┘ [root@xxxx ttl_table_t2] total 8 drwxr-x drwxr-x drwxr-x -rw-r
在写入数据后,每个分区目录内都会生成ttl.txt文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 tree ./202007_1_1_0 . ├── checksums.txt ├── code.bin ├── code.mrk2 ├── columns.txt ├── count.txt ├── create_time.bin ├── create_time.mrk2 ├── id.bin ├── id.mrk2 ├── minmax_create_time.idx ├── partition.dat ├── primary.idx ├── ttl.txt ├── type.bin └── type.mrk2 cat ttl.txt ttl format version: 1 {"columns" :[{"name" :"code" ,"min" :1595917713,"max" :1595917713},{"name" :"type" ,"min" :1595917713,"max" :1595917713}],"table" :{"min" :1596004103,"max" :1596004103}}
通过ttl.txt内容可以发现,MergeTree时通过一串JSON配置保存了ttl的相关信息,其中:
columns:用于保存列级别ttl的信息
table:用于表示表级别ttl的信息
min和max:保存了当前数据分区内,TTL指定日期字段的最小值、最大值分别与INVERTAL表达式计算后的时间戳。
将table属性中的min和max时间戳格式化,并分别和create_time最小值与最大值对比:
1 2 3 4 5 6 7 8 9 10 SELECT toDateTime('1596004103' ) AS ttl_min, toDateTime('1596004103' ) AS ttl_max, ttl_min - MIN (create_time) AS expire_min, ttl_max - MAX (create_time) AS expire_max FROM ttl_table_t2┌─────────────ttl_min─┬─────────────ttl_max─┬─expire_min─┬─expire_max─┐ │ 2020 -07 -29 14 :28 :23 │ 2020 -07 -29 14 :28 :23 │ 86400 │ 86400 │ └─────────────────────┴─────────────────────┴────────────┴────────────┘
TTL的处理逻辑:
MergeTree以分区目录为单位,通过分区内的ttl.txt文件记录过期时间,并最为后续的判断依据
当写入一批数据时,都会给予INTERVAL表达式的计算结果为这个分区生成对应的ttl.txt文件
在MergeTree进行合并分区时,会触发删除TTL过期数据的逻辑
在选择删除的分区时,使用了贪婪算法:尽可能找到会最早过期,合并次数最多的分区(MaxBlockNum)最大
如果一个分区内某一列数据因为TTL到期被全部删除,在合并之后的新分区目录中,不会包含这个列字段的数据文件(column.bin和column.mrk)
ClickHouse针对列级别和表级别的TTL目前都没有提供删除TTL策略的方法,仅提供了全局开启/关闭TTL的方法:SYSTEM START/STOP TTL MERGES;
,而且该配置并不能指定某张表开启或关闭TTL。