Clickhouse 系列 - 第五章 - 存储引擎

本系列的前四章已经对 clickhouse 的存储引擎设计的思想进行了详细解读,本章将向读者完整地介绍 clickhouse 存储引擎的存储模型及工作机制。

文件组织

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

Clickhouse 系列 - 第五章 - 存储引擎_第1张图片

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。 ### 数据文件结构 Clickhouse 系列 - 第五章 - 存储引擎_第2张图片 数据文件结构 上图展示了一个 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 文件的截图,已经对头信息进行了颜色标记。 Clickhouse 系列 - 第五章 - 存储引擎_第3张图片 通过读取头信息,我们知道该数据库是使用 LZ4 算法压缩,压缩后大小为 0x000049AC 即 18860 字节。压缩前大小为 0x00010000 即 65536 字节,压缩比约为 1:3.4≈29%。图中显示的是 AC490000,由于 clickhouse 使用小端字节序,因此实际为 0x000049AC。 接着,我们从 data 区起始地址开始复制 18860 字节的压缩后数据,将其通过 LZ4 算法解压,可以得到原始数据,如下图所示。解压缩程序的源码已经上传到 github, 读者可以访问该仓库,自行下载实验。github 地址: cfcz48/lz4compress (github.com) Clickhouse 系列 - 第五章 - 存储引擎_第4张图片 解压后的原始数据 接下来,我们通过查询 columns.txt,可以看出 UserAgent 列是 UInt8 的数据类型,也就是说每个数据时无符号的 8 位整数。因此结合上一步得到的原始数据,每 8 位即为一行数据。例如 0x04 表示十进制的 4。即上图中的原始数据为 4。下图展示了前 20 位原始数据解码后的结果: Clickhouse 系列 - 第五章 - 存储引擎_第5张图片 前 20 位解码后结果 我们在用 SQL 查询前 20 位数据,看看是否一致: Clickhouse 系列 - 第五章 - 存储引擎_第6张图片 总结 -- 本章对 clickhouse 的存储引擎的文件组织进行了详细地说明,并给出了一个例子,方便读者理解。下一章将为读者详细介绍数据插入的过程。

你可能感兴趣的:(clickhouse数据仓库)