目录
1、MergeTree的创建方式与存储结构
1.1、MergeTree的创建方式
1.2、MergeTree的存储结构
2、数据分区
2.1、数据分区规则
2.2、分区目录的命令规则
2.3、分区目录的合并过程
1、MergeTree的创建方式与存储结构
1.1、MergeTree的创建方式
创建MergeTree数据表需要将ENGINE参数声明为MergeTree(),其完整的语法如下所示:
CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
(
name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1] [TTL expr1],
name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2] [TTL expr2],
...
INDEX index_name1 expr1 TYPE type1(...) GRANULARITY value1,
INDEX index_name2 expr2 TYPE type2(...) GRANULARITY value2
) ENGINE = MergeTree()
ORDER BY expr
[PARTITION BY expr]
[PRIMARY KEY expr]
[SAMPLE BY expr]
[TTL expr [DELETE|TO DISK 'xxx'|TO VOLUME 'xxx'], ...]
[SETTINGS name=value, ...]
- PARTITION BY [选填]:分区键,用于指定表数据以何种标准进行分区。分区键既可以是单个列字段,也可以通过元组的形式使用多个列字段,同时它也支持使用列表达式。如果不声明分区键,则ClickHouse会生成一个名叫all的分区。合理使用数据分区,可以减少查询时文件的扫描范围。
- ORDER BY [必填]:排序键,用于指定在一个数据片段内,数据以何种标准排序。默认情况下主键(PAIMARY KEY)与排序键相同。排序键既可以是单个列字段,也可以通过元组的形式使用多个列字段。
- PAIMARY KEY [选填]:主键,顾名思义,声明后会按照主键生成一级索引,用于加速查询。默认情况下,主键和排序键(order by)相同,所以通常直接使用order by 代为指定主键,无须刻意通过primary key声明。在一般情况下,在单个数据片段内,数据与一级索引以相同的规则升序排列。与其他数据库不同,MergeTree主键允许存在重复数据(ReplacingMergeTree可以去重)。
- SAMPLE BY [选填]:抽样表达式,用于声明数据是以何种标准进行采样。如果用了这个配置,那么在主键中也要声明同样的表达式。
- SETTINGS:index_granularity [选填]: index_granularity对MergeTree是一个很重要的参数,它表示索引的粒度,默认是8192。也就是说,MergeTree的索引在默认情况下,每间隔8192行数据才生成一条索引。
- SETTINGS:index_granularity_bytes [选填]:在19.11版本之前,clickhouse只支持固定大小的索引间隔,由index_granularity控制,默认是8192。在新版本,它增加了自适应间隔大小的特性,即根据每一批次写入数据的体量大小,动态划分间隔大小。而数据的体量大小由index_granularity_bytes控制,默认是10M(10x1024x1024),设置为0表示不启动自适应功能。
- SETTINGS:enable_mixed_granularity_parts [选填]:设置是否开启自适应索引时间间隔的功能,默认是开启。
- SETTINGS:merge_with_ttl_timeout [选填]:从19.6版本开始,MergeTree提供了数据TTL的功能。
- SETTINGS:storage_policy [选填]:从19.15版本开始,MergeTree提供了多路径的存储策略。
1.2、MergeTree的存储结构
MergeTree表引擎中的数据是拥有物理存储的,数据会按照分区目录的形式保存到磁盘之上,完整的存储结构如下:
table_name
partition_1
checksums.txt
columns.txt
count.txt
primary.idx
[colume].bin
[column].mrk
[column].mrk2
patition.dat
minmax_[column].idx
skp_idx_[column].idx
skp_idx_[column].mrk
partition_2
partiton_n
从上面可以看出一张完整的数据表的完整物理结构为为3个层级,依次是数据表目录、分区目录及各分区下具体的数据文件。依次介绍它们的作用
(1)partition: 分区目录,余下各类数据文件(parmary.idx、[column].mrk、[colume].bin等)都是以分区目录的形式被组织存放,属于相同分区的数据,最终会被合并到同一个分区目录,而不同分区的数据,永远不会被合并在一起。
(2)checksums.txt:校验文件,使用二进制格式存储。保存了其他文件的size大小及size的哈希值,用于快速校验文件的完整性和正确性。
(3)columns.txt: 列信息文件,使用明文格式存储,用于保存此数据分区下的列字段信息。
(4)count.txt:计数文件,使用明文格式存储。用于记录当前数据分区目录下数据的总行数。
(5)primary.idx:一级索引文件,使用二进制格式存储。用于存放稀疏索引,一张MergeTree表只能声明一次一级索引。
(6)[column].bin:数据文件,使用压缩格式存储,默认为LZ4压缩格式,用于存储某一列的数据。
(7)[colume].mrk:列字段标记文件,使用二进制格式存储。标记文件中保存列.bin文件中数据的偏移量信息。标记文件与稀疏索引对齐,又与.bin文件一一对应。
(8)[column].mrk2:如果使用了自适应大小的索引间隔,则标记文件会以.mrk2命名。它的工作原理和作用与.mrk标记文件相同。
(9)partition.dat与minmax_[column].idx:如果使用了分区键,例如partition by eventtime,则会额外生成partition.dat 和minmax索引文件,它们均使用二进制格式存储。partition.dat用于保存当前分区表达式最终生成的值;而minmax索引用于记录当前分区字段对应原始数据的最小和最大值。
(10)skp_idx_[column].idx与skp_idx_[column].mrk;如果在建表语句中声明了二级索引,则会额外生成相应的二级索引与标记文件,它们同样也使用二进制存储。二级索引在clickhouse中又称为跳数索引,目前拥有minmax,set,ngrambf_v1和tokenbf_v1四种类型。
2、数据分区
数据是以分区目录的形式进行组织的,每个分区独立分开存储。借助这种形式,在对mergetree进行数据查询时,可以有效跳过无用的数据文件,只使用最小的分区目录子集。
2.1、数据分区规则
分区id目前有四种生成逻辑:
- 不指定分区键:不使用分区键,分区id默认取名all,所有数据都会被写入这个all分区。
- 使用整型
- 使用日期类型
- 使用其他类型:如果分区键取值既不属于整型也不属于日期类型,则通过128位hash算法取其hash值作为分区id的取值。
2.2、分区目录的命令规则
分区目录名:201905_1_1_0
201905表示分区目录的id;1_1分别表示最小的数据块编号(minblocknum)与最大的数据块编号(maxblocknum);最后的_0(level)则表示目前合并的层级。
2.3、分区目录的合并过程
201905_1_1_0
201905_2_2_0 ----->合并后 201905_1_3_1
201905_3_3_0
mergetree的分区目录并不是在数据表被创建之后就存在的,而是在数据写入过程中被创建的。也就是说如果一张表不写入任何数据,那么也不会有任何分区目录存在。
mergetree中伴随着每一批数据的写入(一次insert语句),都会生成一批新的分区目录。即使不同批次的数据属于相同分区,也会生成不同的分区目录。
clickhouse会通过后台任务再将属于相同分区的多个目录合并为一个新的目录,已经存在的旧的分区并不会立即被删除,而是在之后的某个时刻通过后台任务被删除(默认8分钟)。
新目录名称的合并规则:
minblocknum:取同一分区内所有目录中最小的minblocknum值
maxblocknum:取同一分区内所有目录中最大的maxblocknum值
level:取同一分区内最大的level值加1