Clickhouse MergeTree 原理(一)

俊达2年前技术文章2005

MergeTree是Clickhouse里最核心的存储引擎。Clickhouse里有一系列以MergeTree为基础的引擎(见下图),理解了基础MergeTree,就能理解整个系列的MergeTree引擎的核心原理。


mergetree-engines.png


本文对MergeTree的基本原理进行介绍。

一、MergeTree存储结构


1.1 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, ...]


2、关键属性说明


  • [partition by expr] : 分区键,分区键可以指定一个或多个字段,若不指定分区键时默认为其生成一个名为all的分区。[选填]

  • [order by expr] : 排序键,指定一个数据段内的数据排序规则。默认情况下主键与排序键相同。排序键可以是一个或多个字段。[必填]

  • [primary key expr] : 主键,若设置表primary key,表数据会按照主键字段生成一级索引;若无显式执行primary key,则使用order by字段作为主键排序。MergeTree主键允许重复数据。[选填]

  • [sample by expr] : 抽样表达式,声明使用何种方式进行抽样采集。[选填]


上面的这些属性,只有Order by是必填的。


下面是一个具体的例子:

CREATE TABLE local.metrics
(
    `tt` DateTime,
    `tags` Map(String, String),
    `metric` String,
    `value` Float64,
    `str_value` String
)
ENGINE = MergeTree
PARTITION BY toYYYYMMDD(tt)
ORDER BY (metric, tt)
SETTINGS index_granularity = 8192

1.2 MergeTree物理存储结构


1、Clickhouse中,一个MergeTree引擎表,由一个或多个分区(partition)组成。如果建表时没有制定分区条件,则所有的数据都位于同一个分区。

2、每一个分区,由1个或多个part组成。每一个part,对应clickhouse数据目录中的一个目录,该目录下存储了part对应的数据。

3、part是clickhouse数据存储、数据复制、数据合并的基本单位。每次insert数据,会写入到单独的part中。

4、part的数据一旦写入,就不会发生变化。只有在数据合并时,才会将被合并的part设置为inactive,等后台进程清理。

5、数据合并时,会对同一个分区(partition)中的part进行合并。不同分区的数据不会合并到一起。


我们可以通过system库中的parts表查看part信息。


ck01 :) select * from system.parts where table='metrics'\G

SELECT *
FROM system.parts
WHERE table = 'metrics'

Query id: 2948f29c-1f23-4f5e-b9a5-ac6006ce5383

Row 1:
──────
partition:                             20221129
name:                                  20221129_1_4_2
uuid:                                  00000000-0000-0000-0000-000000000000
part_type:                             Compact
active:                                1
marks:                                 2
rows:                                  3
bytes_on_disk:                         412
data_compressed_bytes:                 203
data_uncompressed_bytes:               92
marks_bytes:                           176
min_block_number:                      1
max_block_number:                      4
level:                                 2
data_version:                          1
primary_key_bytes_in_memory:           36
primary_key_bytes_in_memory_allocated: 8256
is_frozen:                             0
database:                              local
table:                                 metrics
engine:                                MergeTree
disk_name:                             default
path:                                  /data/clickhouse/clickhouse/store/def/def88518-fd7b-418d-a7dd-6564e38bba39/20221129_1_4_2/
...


分区目录命名规则

分区目录的命名规范为: PartitionID_MinBlockNum_MaxBlockNum_Level

  • PartitionID : 分区ID。

  • MinBlockNum、MaxBlockNum : 最小数据块编号、最大数据块编号,数据块编号由1开始自增长。

  • Level :  合并操作层级,随着合并的次数递增。

分区目录内容

mergetree-part.png


  • checksums.txt : 校验文件,使用二进制格式存储。记录了各类文件的大小以及大小的hash值

  • columns.txt : 列信息文件,使用明文存格式储。存储了该分区下的表字段信息。

  • count.txt : 计数文件,存储了当前分区下的数据行数。

  • default_compression_codec.txt :

  • [column].bin : 列字段数据文件,默认使用LZ4格式压缩存储。

  • [column].mrk2 : 列字段标记文件,使用二进制格式存储,标记文件中保存了[column].bin文件中数据的偏移量。标记文件是一级索引文件与数据文件之间进行关联的桥梁。

  • primary.idx : 一级索引文件,使用二进制格式存储。存储了该分区的稀疏索引,MergeTree通过primary by或order by声明一级索引的定义。

  • skip_idx[column].idx、skip_idx[column].mrk2 : 如果建表语句中声明了相关的二级索引(跳数索引),则会生成相关二级索引的索引文件与标记文件。


clickhouse part数据存储分两种格式:

* compact: 所有字段的数据都存储道data.bin中。如上图中part的格式就是compact。

* wide: 每个字段都存储到单独的文件中


存储格式受参数min_bytes_for_wide_part和min_rows_for_wide_part控制。只有当纪录数或记录占用的空间超过配置参数,才以wide格式存储。


part合并过程


当多个同分区的分区目录进行合并时:


  • 分区ID相同

  • MinBlockNum取所有待合并分区目录中最小的MinBlockNum值

  • MaxBlockNum取所有待合并分区目录中最大的MaxBlockNum值

  • Level取所有待合并分区目录中最大Level+1



相关文章

Elasticsearch如何使用内存

ES作为一个JAVA程序,其对内存的使用和管理依赖底层JVM。因而设置内存时需要遵从JAVA的普适原则,如-xmx和-xms设置为相同值等。在JVM的基础上,ES对内存的使用可按功能分为以下几大部分:...

mysql高可用配置(一)

一、简介MySQL使用双向半同步复制模式,通过开源的keepalived实现自动切换,应用通过vip连接数据库。配合自定义脚本,实现故障安全切换,切换过程对应用透明。二、部署主从2.1、在主备节点部署...

datahub安装部署

datahub安装部署

背景:由于某客户需要建立sparksql的血缘关系,于是乎提出datahub,由于网上关于datahub资料较少,因此这里做以记录datahub作为一个元数据管理平台,他可以对数据资产进行有效的组织,...

MySQL运维实战(3.1) MySQL官方客户端使用介绍

mysql是mysql安装包默认的客户端。位于二进制安装包的bin目录。或者通过rpm安装包安装mysql-community-client。使用mysql程序linux终端下,输入mysql命令登陆...

Trino对接haproxy开启ssl

Trino对接haproxy开启ssl

1、背景客户在开启https后,在高并发的情况下,集群性能下降严重。初步估计是由于worker和Coordinator交互都需要进行加密和解密,会提高cpu使用率,导致服务变慢。现在采用haproxy...

ES字段类型与内存管理

ES字段类型与内存管理

一、ES常见字段类型:1、 概述字段是数据存储的最小微粒,根据数据的性质不同将数据分成不同的字段类型,熟悉不同字段类型的特性,对索引的Mapping设计、查询调优都极其重要。2、 关键参数Index:...

发表评论    

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。