创立形式和存储构造
Mergetree 在写入数据时,数据总会以数据片段的模式写入磁盘,为了防止片段过多,ClickHouse 会通过后盾线程,定期合并这些数据片段,属于雷同分区的数据片段会被合并成一个新的片段,正式合并树名称的由来。
创立形式
CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
(name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1],
name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2],
...
INDEX index_name1 expr1 TYPE type1(...) GRANULARITY value1,
INDEX index_name2 expr2 TYPE type2(...) GRANULARITY value2
) ENGINE = MergeTree()
[PARTITION BY expr]
[ORDER BY expr]
[PRIMARY KEY expr]
[SAMPLE BY expr]
[SETTINGS name=value, ...]
主要参数:
PARTITION BY
分区键,不申明分区键,则会默认生成一个名为 all 的分区。
ORDER BY
必填
,排序键,默认状况下主键与排序键雷同。
PRIMARY KEY
会依据主键字段生成一级索引,用于减速查问,可不申明,默认是 ORDER BY 定义的字段。
SAMPLE BY
抽样表达式,申明数据以何种规范进行采样,如果应用此配置,必须子主键的配置中也申明同样的表达式。
ORDER BY (CounterID,intHash32(UserID))
SAMPLE BY intHash32(UserID)
SETTINGS
index_granularity: 索引粒度,默认 8192,也就每隔 8192 行才生成一条索引
enable_mixed_granularity_parts: 是否开启自适应索引距离性能,默认开启
index_granularity_bytes: 索引粒度,依据每一批次写入数据的大小,动静划分距离大小,默认 10M(10*1024*1024
)
存储构造
创立测试表
CREATE TABLE test.part_v1
(
`ID` String,
`URL` String,
`age` UInt8 DEFAULT 0,
`EventTime` Date
)
ENGINE = MergeTree()
PARTITION BY toYYYYMMDD(EventTime)
ORDER BY ID
SETTINGS index_granularity = 8192
插入数据
insert into test.part_v1 values
('A001', 'www.test1.com', 1,'2020-08-01')
('A001', 'www.test1.com', 1,'2020-08-02')
('A002', 'www.test1.com', 1,'2020-08-03');
查看目录构造
[root@test 20200801_12_12_0]# ll
总用量 56
-rw-r----- 1 clickhouse clickhouse 29 8 月 18 15:56 age.bin
-rw-r----- 1 clickhouse clickhouse 48 8 月 18 15:56 age.mrk2
-rw-r----- 1 clickhouse clickhouse 456 8 月 18 15:56 checksums.txt
-rw-r----- 1 clickhouse clickhouse 91 8 月 18 15:56 columns.txt
-rw-r----- 1 clickhouse clickhouse 1 8 月 18 15:56 count.txt
-rw-r----- 1 clickhouse clickhouse 32 8 月 18 15:56 EventTime.bin
-rw-r----- 1 clickhouse clickhouse 48 8 月 18 15:56 EventTime.mrk2
-rw-r----- 1 clickhouse clickhouse 42 8 月 18 15:56 ID.bin
-rw-r----- 1 clickhouse clickhouse 48 8 月 18 15:56 ID.mrk2
-rw-r----- 1 clickhouse clickhouse 4 8 月 18 15:56 minmax_EventTime.idx
-rw-r----- 1 clickhouse clickhouse 4 8 月 18 15:56 partition.dat
-rw-r----- 1 clickhouse clickhouse 10 8 月 18 15:56 primary.idx
-rw-r----- 1 clickhouse clickhouse 49 8 月 18 15:56 URL.bin
-rw-r----- 1 clickhouse clickhouse 48 8 月 18 15:56 URL.mrk2
[root@test 20200801_12_12_0]# pwd
/var/lib/clickhouse/data/test/part_v1/20200801_12_12_0
目录档次:数据库名 > 数据表名 > 分区目录 > 分区下具体文件
20200801_12_12_0 是分区名
.txt 是明文存储,.bin/.dex/.mrk 二进制存储
- partition.dat: 分区信息
- checksum.txt: 数据校验信息
- columns.txt: 列信息
- count.txt: 计数信息
- primary.idx: 一级索引信息,用于存储稠密索引信息
- [column].bin: 存储某一列的信息,默认应用 lz4 压缩算法存储
- [column].mrk: 列字段标记问题,保留.bin 文件中数据的偏移量信息
- [column].mrk2: 如果定义了自适应索引,则会呈现该文件,作用和.mrk 文件一样
- partition.dat、minmax_[column].idx: 定义了分区键,会呈现这二个文件,partition 存储以后分区下分区表达式最终生成的值,minmax_[column].idx 记录以后分区下对应原始数据的最小最大值
- skp_idx_[Column].idx 与 skp_idx_[Column].mrk: 二级索引信息
数据分区
数据的分区规定
分区规定由分区 ID 决定,,分区 ID 生成规定有四种逻辑
- 不指定分区键:没有定义 PARTITION BY,分区 ID 默认 all
- 应用整型:间接按该整型的字符串模式输入,做为分区 ID
- 应用日期类型:分区键时日期类型,或者能够转化成日期类型,比方用 today 转化,YYYYMMDD 格局按天分区,YYYYMM 按月分区等
- 应用其余类型:String、Float 类型等,通过 128 位的 Hash 算法取其 Hash 值作为分区 ID
数据进行分区存储,在查问时能够疾速定位数据地位
分区目录的命名规定
分区命名规定,对于 20200801_1_1_0
PartitionID_MinBlockNum_MaxBlockNum_Level
- PartitionID: 分区 ID,20200801 就是分区 ID
- MinBlockNum、MaxBlockNum: 最小分区块编号和最大分区块编号,BlockNum 是整型的自增长编号,从 1 开始,新创建一个分区目录时,会 +1,新创建的分区 MinBlockNum=MaxBlockNum
- Level:合并的层级,被合并的次数
分区目录的合并过程
每次数据 insert 写入,都会生成新的分区目录,在之后的某个时刻(写入后的 10-15 分钟,也能够手动执行 optimize 强制合并)会通过后台任务再将属于雷同分区的多个目录合并成一个新的目录,曾经存在的目录通过后台任务删除(默认 8 分钟)。
合并之后新目录名规定:
- MinBlockNum:取同一分区内所有目录中最小的 MinBlockNum 值
- MaxBlockNum:取同一分区内所有目录中最大的 MaxBlockNum 值
- Level:取同以分区内最大 Level 值并 +1
一级索引
一级索引也就是主键索引,通过 PRIMARY KEY/ORDER BY 定义
会写入 primary.idx 文件中
稠密索引和浓密索引的区别
稠密索引应用一个索引标记一大段时间,缩小了索引的数据量,使得 primary.idx 能够常驻内存,减速数据查问
数据索引的生成过程
PARTITION BYtoYYYYMM(EventDate)),所以 2014 年 3 月份的数据最终会被划分到同一个分区目录内。应用 CounterID 作为主键(ORDER BY CounterID),每距离 8192 行会生成一个主键索引保留到 primary.idx 文件中
压缩数据块
数据标记的生成规定
数据标记是连接一级索引和数据的桥梁
数据标记和索引区间是对齐的,均依照 index_granularity 的粒度距离。只需简略通过索引区间的下标编号就能够间接找到对应的数据标记。每一个列字段[Column].bin 文件都有一个与之对应的[Column].mrk 数据标记文件,用于记录数据在.bin 文件中的偏移量信息
一行标记数据应用一个元组示意,元组内蕴含两个整型数值的偏移量信息。对应的.bin 压缩文件中,压缩数据块的起始偏移量;以及将该数据压缩块解压后,其未压缩数据的起始偏移量
每一行标记数据都示意了一个片段的数据(默认 8192 行)在.bin 压缩文件中的读取地位信息。标记数据与一级索引数据不同,它并不能常驻内存,而是应用 LRU(最近起码应用)缓存策略放慢其取用速度。
分区、索引、标记和压缩数据的协同总结
写入过程
首先生成分区目录,属于雷同分区的目录会按照规定合并到一起
紧接着依照 index_granularity 索引粒度,会别离生成 primary.idx 一级索引(如果申明了二级索引,还会创立二级索引文件)、每一个列字段的.mrk 数据标记和.bin 压缩数据文件
查问过程
查问的实质,能够看作一个一直减小数据范畴的过程。在最现实的状况下,MergeTree 首先能够顺次借助分区索引、一级索引和二级索引,将数据扫描范畴缩至最小。而后再借助数据标记,将 须要解压与计算的数据范畴缩至最小
如果一条查问语句用不到索引会进行分区指标扫描,虽不能放大数据范畴,然而 MergeTree 依然可能借助数据标记,以多线程的模式同时读取多个压缩数据块,以晋升性能
数据标记和压缩数据块的对应关系
每个压缩数据块的体积都被严格控制在 64KB~1MB。而一个距离(index_granularity)的数据,又只会产生一行数据标记,依据一个距离内数据的理论字节大小,数据标记和压缩数据块之间会产生三种不同的对应关系
多对一
多个数据标记对应一个压缩数据块,当一个距离(index_granularity)内的数据未压缩大小 size 小于 64KB 时,会呈现这种对应关系。
一对一
一个数据标记对应一个压缩数据块,当一个距离(index_granularity)内的数据未压缩大小 size 大于 64KB 小于 1M 时,会呈现这种对应关系。
一对多
一个数据标记对应多个压缩数据块,当一个距离(index_granularity)内的数据未压缩大小 size 大于 1M 时,会呈现这种对应关系。
二级索引
二级索引又称跳数索引,由数据的聚合信息构建而成,依据索引类型的不同,其聚合信息的内容也不同。
须要在 CREATE 语句内定义,定义了跳数索引会额定生成相应的索引文件后标记文件
skp_idx_[Column].idx 和 skp_idx_[Column].mrk
1. 定义表构造
CREATE TABLE test.part_v2
(
`ID` String,
`URL` String,
`age` UInt8 DEFAULT 0,
`EventTime` Date
)
ENGINE = MergeTree()
PARTITION BY toYYYYMMDD(EventTime)
ORDER BY ID
SETTINGS index_granularity = 8192
查看文件目录
总用量 4
drwxr-x--- 2 clickhouse clickhouse 6 8 月 20 18:54 detached
-rw-r----- 1 clickhouse clickhouse 1 8 月 20 18:54 format_version.txt
参考资料
《ClickHouse 原理解析与利用实际》