目录完全解析"/>
ClickHouse数据目录完全解析
之前文章有介绍过基础的MergeTree的物理存储结构,数据会按分区目录的形式保存到磁盘。本文着重介绍一些二进制文件的格式及内容。
首先按照如下规则创建表,用于后续数据的对照查询
#创建表
CREATE TABLE default.ansel
(`a` Int32,`b` Int32,`c` Int32,INDEX `idx_c` (c) TYPE minmax GRANULARITY 1
)
ENGINE = MergeTree
PARTITION BY a
ORDER BY b
SETTINGS index_granularity=3, index_granularity_bytes = 0;#插入测试数据
insert into default.ansel(a,b,c) values(3,10,4),(3,9,5),(3,8,6),(3,7,7),(3,6,8),(3,5,9),(3,4,10);
注意:设置index_granularity_bytes = 0取消自适应索引粒度,便于后续观察mrk文件结构。
插入后看一下测试数据如下:
生成的数据文件如下:
[clickhouse@localhost /var/lib/clickhouse/data/default/ansel ]$ tree
├── 3_1_1_0
│ ├── a.bin
│ ├── a.mrk
│ ├── b.bin
│ ├── b.mrk
│ ├── c.bin
│ ├── checksums.txt
│ ├── c.mrk
│ ├── columns.txt
│ ├── count.txt
│ ├── minmax_a.idx
│ ├── partition.dat
│ ├── primary.idx
│ ├── skp_idx_idx_c.idx
│ └── skp_idx_idx_c.mrk
├── detached
└── format_version.txt
一般的数据目录结构如下:
table_name #表名
├─ partition_{index} DIR #分区目录
│ │ # 基础文件
│ ├─ checksums.txt BIN #各类文件的尺寸以及尺寸的散列
│ ├─ columns.txt TXT #列信息
│ ├─ count.txt TXT #当前分区目录下数据总行数
│ ├─ primary.idx BIN #稀疏索引文件
│ ├─ {column}.bin BIN #经压缩的列数据文件,以字段名命名
│ ├─ {column}.mrk BIN #列字段标记文件
│ ├─ {column}.mrk2 BIN #使用自适应索引间隔的标记文件
│ │
│ │ # 分区键文件
│ ├─ partition.dat BIN #当前分区表达式最终值
│ ├─ minmax_{column}.idx BIN #当前分区字段对应原始数据的最值
│ │
│ │ # 跳数索引文件
│ ├─ skp_idx_{column}.idx BIN #跳数索引文件
│ └─ skp_idx_{column}.mrk BIN #跳数索引表及文件
│
└─ partition_{index} DIR #分区目录
-
columns.txt
列信息文件,使用文本文件存储,用于保存分区下的列字段信息。
记录了有多少个字段,及字段名字和类型。 -
count.txt
计数文件,文本文件存储,用于记录当前数据分区目录下数据的总行数。
记录了该part中row的数量。 -
primary.idx
一级索引文件,使用二进制格式存储。
主键索引,根据index_granularity索引粒度,每index_granularity行取一个主键列的值组合起来作为索引,例子中并没有设置PRIMARY KEY,clickhouse在这种情况下默认将ORDER BY的字段默认作为PRIMARY KEY,所以这里的primary.idx是根据b字段的数据进行固定间隔抽取的。例子中index_granularity=3,所以primary.idx中存的是4、7、10。 -
{column}.mrk
列字段标记,使用二进制格式存储。标记文件中保存了bin文件中数据的偏移量信息,mrk文件与稀疏文件对齐,又与bin文件一一对应,所以MergeTree通过标记文件建立了primary.idx稀疏索引与bin数据文件的映射关系
以b.mrk为例
mrk文件分为两列,第一个值对应bin文件中压缩后数据块的偏移量,第二个值对应bin文件解压后数据块的偏移量,单位均为字节。Int32为4字节,索引粒度为3,所以数据块的偏移量是0、12、24。
直观点可以表示成下面规则:
所以primary.idx和mrk的行是一一对应。 -
{column}.bin
数据文件,使用压缩格式存储,默认使用LZ4压缩格式,用于存储某一列的数据。
以b.bin为例
一个压缩数据块由头信息和压缩数据两部分组成。头信息固定使用9位字节表示,具体由1个UInt8(1字节)和2个UInt32(4字节)整型组成,分别代表了使用的压缩算法类型、压缩后的数据大小和压缩前的数据大小。
Checksum:该bin文件的校验值,16字节。
Block:数据块,包含Head和CompressedData。
Head:包含CompressionMethod、CompressedSize、UncompressedSize三部分,其中CompressionMethod类型为UInt8占4字节,包含LZ4(0x82)、ZSTD(0x90)、Multipile(0x91)、Delta(0x92),CompressedSize类型为UInt32占4字节,UncompressedSize类型同样为UInt32占4字节。
CompressedData:压缩数据块,默认最小65535字节/64K,最大1048576字节/1M。
查看bin文件内容可以使用官方的clickhouse-compressor,其余bin文件都可以用这个方法
clickhouse-compressor --decompress < 3_1_1_0/b.bin | od -An -i -w4
还可以通过clickhouse-compressor查看b.bin的统计信息。
clickhouse-compressor --stat < b.bin
其中28表示压缩前数据大小,因为从4-10,每个数字占4字节,一共28字节,39表示压缩后数据大小,因为数据量太小,压缩也要有些必要的字节表示一些元信息,所以会比压缩前大,也可以再插入些数据再观察一下,如:
insert into default.ansel(a,b,c) values(4,10,4),(4,9,5),(4,8,6),(4,7,7),(4,6,8),(4,5,9),(4,4,10),(4,4,11),(4,7,12),(4,4,13),(4,8,14),(4,6,15),(4,4,16),(4,6,17);
可以发现,压缩后是小的,如下:
还回到原始数据的b.bin,看下二进制文件
第一行16个自己就是Checksum值;
第二行第一个字节0x82就是压缩算法,这里就是默认的LZ4,第三到第五字节就是CompressedSize,这里centos7中安装的ck,所以是小端模式,所以要把27 00 00 00反过来看,即00 00 00 27,转换成10进制就是39,所以和上面的clickhouse-compressor中第二个数字39是一致的;继续向后看4个字节,反过来就是00 00 00 1c,即十进制的28,和上面的clickhouse-compressor中第二个数字28是一致的;再向后就涉及LZ4压缩的内容了
还可以通过clickhouse-compressor直接看数据的16进制存储
可以看到从04 00 00 00到0a 00 00 00,反过来看也就是4-10
- partition.dat
用于保存当前分区下分区表达式最终生成值。
记录了该part的partition最终计算值,与3_1_1_0的3是一致的
- minmax_a.idx
用于记录当前分区字段对应原始数据的最小值和最大值
- checksums.txt
校验文件,使用二进制存储,保存了各类文件的size大小和size的哈希值,用于快速校验文件的完整性和正确性。
从中能看到各个column的bin和mrk文件、primary.idx、minmax_a.idx、skp_idx_c.idx、skp_idx_c.mrk的checksum值都会记录在该二进制文件中。
更多精彩内容欢迎关注微信公众号
更多推荐
ClickHouse数据目录完全解析
发布评论