索引技术是一款数据库产品的核心,那么clickhouse是如何来使用索引的呢?我们试着来一窥究竟吧~~
注意:本文所说的索引是指MergeTree引擎下的索引技术。

1. 稀疏索引

首先,clickhouse的一级索引使用了一种叫做稀疏索引的技术,那么何为稀疏索引呢?既然有稀疏索引,是不是相对的也有稠密索引呢?没错,确实有。二者的区别如下:
稠密索引: 每行数据记录都会对应一行索引标记。
稀疏索引: 每隔若干行记录对应一条索引标记。
既然概念清楚了,那么使用稀疏索引带来的好处也是显而易见的,那就是可以大幅减少索引占用的空间。以clcikhouse默认的索引力度8192为例,1亿行数据只需要存储12208行索引。因为占用空间小,clickhouse中一级索引的数据是常驻内存的,所以取用速度极快。
那么稠密索引也有其好处,因为每条记录都有索引,所以查询的时候可以一步到位,当然缺点就是会占用较多的空间,像mysql的主键索引即是使用的稠密索引。

2. 索引粒度

索引粒度对应的是index_granularity这个参数,前一节我们已经说了clickhouse使用的是稀疏索引的方式,那么index_granulariy这个参数就是决定每隔多上行数据生成一条索引记录了,默认是8192,新版本的clickhouse已经提供了自适应粒度大小的特性。

3. 索引文件

clickhouse会在每个分区目录下生成一个索引文件primary.idx,记录了主键排序后按照索引粒度采样的值,以二进制的方式存储,可以通过od命令进行查看:

[root@slave3 20201216_22_22_0]# od -l -j 0 -N 80 --width=8 primary.idx
0000000     1608087600001562
0000010     1608087966741501
0000020     1608087637579224
0000030     1608088172094313
0000040     1608087674594699
0000050     1608087862362366
0000060     1608087712835091
0000070     1608087875942332
0000100     1608087750685288
0000110     1608088251777291
0000120

4. 标记文件

因为是稀疏索引,所以显然只靠一级索引文件是无法精确定位到数据的,这时候就需要标记文件登场了。在分区目录下,你可以看到很多后缀为.bin和.mrk2的文件,其中.bin是真实的数据内容,.mrk2就是我们要说的标记文件。因为clickhouse底层是按列进行存储的,因此每一列会对应一个.bin文件和.mrk2文件。

[root@slave3 20201216_22_22_0]# od -l -j 0 -N 240 --width=24 ./city.mrk2
0000000                    0                    0                 8192
0000030                    0                32768                 8192
0000060                  295                    0                 8192
0000110                  295                32768                 8192
0000140                  590                    0                 8192
0000170                  590                32768                 8192
0000220                  885                    0                 8192
0000250                  885                32768                 8192
0000300                  885                64680                    0
0000330

一行标记数据使用一个元组表示,元组内包含数据压缩块位置(在.bin文件中数据是切分成若干个数据块压缩存储的),数据块内偏移和索引粒度的大小。
标记文件并不能常驻内存,俄日是使用LRU缓存策略加快其读取速度。

5. 索引工作方式

那么clickhouse是如何利用primary.idx和.mr2文件检索到具体的文件内容的呢?首先索引文件和标记文件在行上是对齐的,从上面索引文件和标记文件的示例可以看出来,二者的行数是一样的,这里我用网上的一张图来说明吧:

clickhouse 查看索引列表 clickhouse添加索引_数据


我们在查询的时候,会先根据要索引的值或范围,在primary.idx文件中确定一个行号范围(具体确定的过程这里就不详细展开了,基本上就是一个递归交集的判断),然后按照相同的行号范围在每一列的.mrk中查询,得到要查询的值在数据文件.bin的哪一个压缩块,以及将该压缩块解压之后在什么位置,然后将查询到的数据结果返回。

通过partition + 一级索引 + 标记文件,层层缩小数据扫描范围,clickhouse达到了其快速检索的目的。

疑问:上面所说的都是查询条件命中索引的情况,如果没有的话clickhouse是怎么处理的呢?
当然只能是每个partition挨个扫描了,不过因为.bin文件分了若干个小的压缩块,clickhouse利用多线程读取压缩块的方式在一定程度上也可以加速查找过程。

希望上面的介绍能让大家对clickhouse的索引有一个大概的了解吧!