上一篇文章介绍了ClickHouse表的引擎,内容比较少。也比较好消化,而ClickHouse中最强大的为MergeTree(合并树)引擎系列(*MergeTree)中的其他引擎。当有非常大的数据量插入到表中,要高效地一批批写入数据片段,并希望这些数据片段在后台按照一定规则合并。相比在插入时不断修改数据进行存储,MergeTree引擎会高效很多。
MergeTree的创建方式与存储结构
建表sql以及插入sql
1 | -- 建表sql |
运行以上sql成功后可以在cd /var/lib/clickhouse/data/default/demo1_table/
目录中看到如下内容
1 | 202106_1_1_0 202106_2_2_0 202107_3_3_0 detached format_version.txt |
MergeTree的存储结构:随便进入一个目录,例如202107_3_3_0
,在该目录下会看到如下文件checksums.txt、columns.txt、count.txt、date.bin、date.mrk2、id.bin、id.mrk2、minmax_date.idx、name.bin、name.mrk2、partition.dat、primary.idx
- checksums.txt:二进制的校验文件,保存了余下文件的大小size和size的Hash值,用于快速校验文件的完整和正确性
- columns.txt:明文的列信息文件
- *.bin:压缩格式(默认LZ4)的数据文件,保存了原始数据。以列名.bin命名
- *.mrk2:mrk2文件存储的是bin文件中数据位置的映射关系
- primary.idx:二进制的一级索引文件,在建表的时候通过OrderBy或者PrimaryKey声明的稀疏索引
数据分区
数据是以分区目录的形式组织的,每个分区独立分开存储。查询数据时,可以有效的跳过无用的数据文件。数据的分区规则如下:
分区键的取值,生成分区ID,分区根据ID决定。根据分区键的数据类型不同,分区ID的生成目前有四种规则:
- 不指定分区键
- 使用整形
- 使用日期类型 toYYYYMM(date)
- 使用其他类型
在数据写入时,会对照分区ID落入对应的分区。例如partitionID_MinBlockNum_MaxBlockNum_Level
BlockNum是一个全局整型,从1开始,每当新创建一个分区目录,此数字就累加1;MinBlockNum是最小数据块编号;MaxBlockNum是最大数据块编号。
对于一个新的分区,MinBlockNum和MaxBlockNum的值相同。Level则代表合并的层级,即某个分区被合并过的次数。不是全局的,而是针对某一个分区。
MergeTree的数据分区也有合并过程。不同的批次写入数据属于同一分区,也会生成不同的目录,在之后的某个时刻再合并(写入后的10~15分钟),合并后的旧分区目录默认8分钟后删除。
同一分区的多个目录合并以后的命名规则如下:
- MinBlockNum:取同一分区中MinBlockNum值最小的
- MaxBlockNum:取同一分区中MaxBlockNum值最大的
- Level:取同一分区最大的Level值加1
索引
索引的文件为:primary.idx
MergeTree的主键使用Primary Key定义,主键定义之后,MergeTree会根据index_granularity间隔(默认8192)为数据生成一级索引并保存至primary.idx文件中。这种方式称为稀疏索引。
也可以通过通过order by指代主键。稀疏索引:每一行索引标记对应一段数据记录(默认索引粒度为8192);稠密索引:每一行索引标记对应一行具体的数据记录。稀疏索引占用空间小,取用速度快。
MergeTree的TTL
TTL表示数据的存活时间,即可以设置在表上,也可以设置在列上。TTL指定的时间到期后则删除相应的表或列,如果同时设置了TTL,则先删除过期时间相应数据。TTL设置在列上如下所示
1 | -- 建表语句 |
TTL设置在表上如下所示
1 | create table tt1_table_v2 ( |