聊下 Clickhouse MergeTree 的分区,索引,标记和压缩数据
今天花了一天把 《ClickHouse 原理解析与应用实践》过了一遍,除了感叹诸多结构都为了节省每一个 byte 做到极致,也感受到要理解某些设计又一点压力。
看完之后我感觉差不多理解了,抽几个重点略微总结一下,日后忘了可回来看看。
数据的查询过程
数据的查询过程,我通过书上的这张图来说,要理解 .mrk .primary .bin 我感觉需要完全理解这个图。
这张图表述如何连接 .mkr和 .bin文件。也就是说如何通过标记文件来寻找对应的压缩块在哪儿。
.mrk 中保存的是解压缩块中的偏移量位置,和压缩块中的偏移量位置。这里让我们慢一点来描述这个问题。
1. 压缩块中偏移量我们一直要读到和 0 不一致,最终我们得到 [0, 12016)这个区间。这里我解释一下为什么有 8 个偏移量为 0 的元素。这里 JavaEnable 是 UInt8 型字段 占用 1Byte 空间。
一个块大概有 65536 Byte 64KB 空间,JavaEnable 8192 行正好可以占据 8192 Byte 空间,那么要填满一个块 65536 Byte / 8192 Byte = 8 个空间。所以得到这里 8 个元素都为第 0 个块。那你可能要问了,我不是压缩的数据吗?但是要知道 压缩块偏移量和解压缩块偏移量是一一对应的。当再增加数据,解压缩块偏移量就不够用了就需要换一个块了。自然记录压缩块偏移量的 offset 就变化了。如果没有理解,停下来再想想。
2. 解压缩块中的偏移量我们也要一直读到和 0 不一致,最终我们得到了 [0, 8192] 这个区间。但是对应着一个完整的块我们应该是 [0, 65536) 这个区间。
总结一下 .mkr 描述文件块 0,一个获取压缩之后的位置偏移量,一个获取解压缩之后的位置偏移量。注意「压缩块中的偏移量」和 「解压缩块中的偏移量」这两个是一一对应的,最终.mrk 你会得到这样一个东西。
编号 | 压缩块中的偏移量 | 解压缩块中的偏移量 |
0 | 0 | 0 |
1 | 0 | 8192 |
2 | 0 | 16384 |
3 | 0 | 24576 |
4 | 0 | 32768 |
5 | 0 | 40960 |
6 | 0 | 49152 |
7 | 0 | 57344 |
8 | 12016 | 0 |
3. 然后回到 .bin 文件中寻找对应块需要结合 primary.idx, 注意 primary.idx 同样和 .mrk 是一一对应的。所以我们会得到这么一个东西。
编号 | CounterID |
0 | 57 |
1 | 1635 |
2 | 3266 |
所以当我们要查一个主键 CounterID = 1000 的数据是怎么查的呢?
1. 根据 where CounterID = 1000 得到我们 primary.idx 中需要得到编号0 到编号1 这个区间里面的东西 [57, 1635)。
2. 然后找到 .mrk 编号为 0 对应的块 offset 这里压缩块 offset 就是 0 。
3. 根据该 offset 找到压缩块位置,读取整个块解压然后再 2 分查找 CounterID = 1000 的记录即可(没看代码,猜测可以用二分因为有顺序)。
另外还想提一点的是,当我们使用了 select * from xxx where CountID=1000 我们依然会获得加速,因为虽然每个字段都有自己的 .bin 数据文件,但是他们的编号会跟主键上的一一对应。你通过 primary.idx 取得了对应编号后,也就相当于查到了其他字段的编号。所以查询也能被加速。就像下面这个图
其他字段并不有序,所以要先通过 primary key 拿到对应的序号才行。
数据的写入过程
首先我们假设我们创建的是一张带分区表。
8873898 行记录通过 34 批次写入合并了三次。所以最后我们得到的文件是 201403 分区 1 最小块 34 最大块 3次合并。 这是 201403_1_34_3 数据块的由来。
默认配置 index_granularity = 8192 当我们写入数据之后,会每 8192 行生成 1 个稀疏索引。
1. 所以第 1 排待写入数据就是稀疏索引的情况,「数据间隔0」「数据间隔1」「数据间隔2」「数据间隔3」。
2. 第 2 排写 primary.idx(一级索引)存储一级索引和索引标记的关系。
3. 维护标记,记录压缩块和非压缩块位置。
4. 如果数据行 8192 行并不能占据满 64KB 块大小空间,那么可以继续留着下一个 8192 行使用。 如果 8192 行数据在 64KB 到 1MB 这个区间,那么直接使用一个块。如果超过 1MB 就使用
多个块来维护 8192 行数据即可。
行差不多,另外一部分重要的分区以及分片,得等我深度测试一下再说。