本章将向读者残缺地介绍clickhouse存储引擎的存储模型及工作机制

文件组织

在大部分的DBMS中,数据库实质上就是一个由各种子目录和文件组成的文件目录,clickhouse当然也不例外。clickhouse默认数据目录在/var/lib/clickhouse/data目录中。所有的数据库都会在该目录中创立一个子文件夹。下图展现了clickhouse对数据文件的组织。

每一个数据库都会在clickhouse的data目录中创立一个子目录,clickhouse默认携带default和system两个数据库。default顾名思义就是默认数据库,system是存储clickhouse服务器相干信息的数据库,例如连接数、资源占用等。

上图展现了clickhouse通过官网提供的入门教程操作后,导入的数据库的文件模式。能够看到,在tutorial文件夹中,建设了两个子目录,每个子目录为即为一张数据表。进入hits_v1目录后又能看到两个子目录和一个文本文件。

分区目录

分区目录下的子目录和文件的含意如下:

目录名类型阐明
201403_1_10_2目录分区目录一个或多个,因为分区+LSM生成的
detached目录通过DETACH语句卸载后的表分区寄存地位
format_version.txt文本文件纯文本,记录存储的格局

分区目录形成

分区目录的形成,依照 分区ID_最小数据块编号_最大数据块编号_层级形成。在本例中,分区ID是201403,最小数据块编号是1,最大数据库编号是10,层级是2。数据块编号从1开始自增,新创建的数据库最大和最小编号雷同,当产生合并时会将其批改为合并的数据块编号。同时每次合并都会将层级减少1。对于合并的详情能够查看本系列的番外篇——LSM。

分区ID由用户在创立表时制订,容许用户创立多个分区键,每个分区键之间用‘-’相连。在本例中只应用了一个分区键,即工夫字段,依照年月分区。分区的益处在于进步并发度和减速局部查问。

数据目录
进入分区目录后,就能看到数据实在存储的数据目录的构造了。

columns.txt
该文件是一个文本文件,存储了表构造信息,能够用文本编辑关上。

count.txt
该文件也是一个文本文件,存储了该分区下的行数。能够用文本文件关上。在用户执行select count() from xxx时实质上就是间接返回了该文件的内容,而不须要遍历数据。因而clickhouse的count()的速度十分快。

同时,这边也比照一下MySQL和PostgreSQL的实现,在上述两个关系型数据库中,其罕用的存储引擎,都没有应用clickhouse的这种计划。读者们是否答复出为了MySQL或pg要舍弃简略的计划而应用遍历么?

这个问题的答案是因为事务的可见性,MySQL和pg都是用MVCC机制的事务控制技术,这意味着对于不同事务中执行select count(*)的后果是不同的,对于A事务中执行的insert或delete,对于本事务中是可见的,也就是说在本事务中执行的count是计算了insert和delete影响的。而对于B事务,在A事务提交前,是不能看到A事务中对数据的操作的。因而AB两个事务中执行的count后的后果可能是不同的。如果应用clickhouse的计划,就无奈实现上述需要。而clickhouse则不须要反对事务,因而应用了绝对简略的计划。

primary.idx
主键索引,详情请参考本系列第四章。

checksums.txt
二进制文件,校验和。用于疾速校验数据是否被篡改。、

default_compression_codec.txt
新版本减少的一个文件,在旧版本时无。该文件是一个文本文件,存储了数据文件中应用的压缩编码器。clickhouse提供了多种压缩算法供用户抉择,默认应用LZ4。

[column].mrk3
列的标记文件,详情请参考本系列第四章。

[column].bin
真正存储数据的数据文件。下一节将对其对深入分析。每一列都会生成一个独自的bin文件。

skp_idx_[column].idx
跳数索引,在应用了二级索引时会生成。否则这不生成

skp_idx_[column].mrk
跳数索引标记文件,在应用了二级索引时会生成。否则这不生成

本节具体介绍clickhouse中存储引擎的文件组织。将一张表拆解到具体的目录和文件中,读者应该能对clickhouse的存储系统有了初步理解。下一节将向读者展现如何读懂数据文件。

数据组织
本节将向读者展现如何读取bin文件。因为bin文件是二进制文件,在读取时须要借助工具,无奈应用文本文件进行读取。在windows操作系统下倡议应用winhex,mac零碎举荐hex friend。

数据文件构造

上图展现了一个bin文件的构造。bin文件应用小端字节序存储。bin文件中按block为单位排列数据,每个block文件有16字节校验和,1字节压缩形式,4字节压缩后大小和4字节的压缩前大小组成。每个block起始地址由如下公式确定:

  1. offset(n)=offset(n-1)+25+压缩后大小 (n>=2)
  2. offset(1)=0

校验和
前16为测验和区域用于疾速验证数据是否残缺。

压缩形式
默认为0x82。clickhouse共反对4种压缩形式,别离为LZ4(0x82)、ZSTD(0x90)、Multiple(0x91)、Delta(0x92)。

压缩后大小
存储在data区域的数据的大小。须要根据此大小计算下一个BLOCK的偏移量。

压缩前大小
data区域存储的数据在压缩前的大小。能够根据此计算压缩比。

data区
data区存储数据,大小为头信息第18~21字节示意的大小。拿到data区数据后,因为是压缩后的,因而无奈间接辨认,须要依照压缩形式进行解压缩后,能力辨认。

example
下图是通过clickhouse通过官网提供的入门教程指引操作后,应用二进制阅读器关上hits_v1表的UserAgent.bin文件的截图,曾经对头信息进行了色彩标记。

通过读取头信息,咱们晓得该数据库是应用LZ4算法压缩,压缩后大小为0x000049AC即18860字节。压缩前大小为0x00010000即65536字节,压缩比约为1:3.4≈29%。图中显示的是AC490000,因为clickhouse应用小端字节序,因而理论为0x000049AC。

接着,咱们从data区起始地址开始复制18860字节的压缩后数据,将其通过LZ4算法解压,能够失去原始数据,如下图所示。解压缩程序的源码曾经上传到github,读者能够拜访该仓库,自行下载试验。github地址:cfcz48/lz4compress (github.com)

接下来,咱们通过查问columns.txt,能够看出UserAgent列是UInt8的数据类型,也就是说每个数据时无符号的8位整数。因而联合上一步失去的原始数据,每8位即为一行数据。例如0x04示意十进制的4。即上图中的原始数据为4。下图展现了前20位原始数据解码后的后果:

咱们在用SQL查问前20位数据,看看是否统一:

总结
本章对clickhouse的存储引擎的文件组织进行了具体地阐明,并给出了一个例子,不便读者了解。下一章将为读者具体介绍数据插入的过程。