前言

什么是索引?在RDBMS中索引是一种单独的、物理的对数据库表中一列或多列的值进行排序的一种存储结构,它是某个表中一列或若干列值的集合和相应的指向表中物理标识这些值的数据页的逻辑指针清单。

简单来说,索引通常包含两部分,即索引键(相当于章节)与指向原始数据的指针(相当于页码)。那么索引的意义就非常重要了:

  • 不加索引,当表中有大量记录时,若要对表进行查询,需要全表搜索,就是将所有记录一一取出,和查询条件进行一一对比,然后返回满足条件的记录,这样做会消耗大量查询时间,并造成大量磁盘I/O操作;
  • 存在索引,先在索引中找到符合查询条件的索引键,最后通过该索引键对应的指针,就能快速定位表中符合条件的记录。

稀疏索引

那么什么是稀疏索引呢?既然有稀疏索引,那么相对应的就会有密集索引。

所谓密集索引,就是对应索引列的每个值都会建立索引,即对应一个指针。如下图

clickhouse 二级索引是什么 clickhouse稀疏索引使用_字段

而稀疏索引呢,只是为索引列的某些值建立索引,所以在查询时对索引列进行筛选,并不一定正好能匹配上索引文件,可能会需要进一步查找,稀疏索引本身只是过滤了大量不必要的查询,如下图

clickhouse 二级索引是什么 clickhouse稀疏索引使用_时间戳_02

稀疏索引的方式是在磁盘空间,内存空间,查找时间等多方面的一个折中。

ClickHouse中的稀疏索引

在ClickHouse中,建表时会指定order by,即底层文件进行存储时,会先将数据按照order by指定的字段进行排序再进行存储,而索引列正是order by后面的字段。

先看下官网的示例:

clickhouse 二级索引是什么 clickhouse稀疏索引使用_时间戳_03

是以(CounterID、Date)两个键来建立索引,先对CounterID进行排序,再次基础上再对Date进行排序,可以看到对应的(CounterId、Date)间隔地生成Marks,例如(a,1)(a,2)(a,3);之后又是根据Marks生成相应的Marks numbers,其中Marks和Marks numbers都是保存在内存中,利于查询的时候快速查找。

这里间隔多少条记录确定一个索引,正是通过index_granularity参数来确定的,当然新的版本中还有一个参数index_granularity_bytes也会影响,具体这里就不赘述了(主要是针对分析超级大宽表的场景中,索引列较多,读取的索引文件大小会膨胀的非常厉害)

那么底层存储文件是如何组织的呢?

clickhouse 二级索引是什么 clickhouse稀疏索引使用_偏移量_04

这里的子目录名84f7f599cf7c5f73a0bdfd814ba756d8_1_75_2,是由分区键,起始mark number和结束mark number组成,这样就可以根据目录有效的定位具体数据。

而子目录下的内容:

clickhouse 二级索引是什么 clickhouse稀疏索引使用_找到符合条件的索引_05

其中,bin文件存储的是每一列的原始数据(被压缩存储),mrk2文件存储的是图中的mark numbers与bin文件中数据位置的映射关系。另外,还有一个primary.idx文件存储被索引列的具体数据

clickhouse 二级索引是什么 clickhouse稀疏索引使用_偏移量_06

图片转载自https://www.jianshu.com/p/f752b3c1a93d

Kafka中的稀疏索引

Kafka中每个日志分段文件对应两个索引文件,其中

  • 偏移量索引文件:建立消息偏移量(offset)到物理地址之间的映射关系,方便定位消息所在的物理文件位置
  • 时间戳索引文件:根据指定的时间戳来查找对应的偏移量信息

Kafka中的索引文件也是以稀疏索引的方式构造消息的索引,所以也并不会保证每个消息在索引文件中都有对应的索引项。每当写入一定量(由参数log.index.interval.bytes指定,默认是4KB)的消息,就会在偏移量索引文件和时间戳索引文件分别增加一个偏移量索引项和时间戳索引项。

  • 偏移量索引文件
    每个索引项占8个字节,relativeOffset:表示相当于baseOffset的偏移量,占用4个字节(当前索引文件的文件名就是baseOffset的值)
    position:物理地址,也就是消息在日志分段文件中对应的物理位置,占用4个字节
  • 时间戳索引文件
    每个索引项占12个字节,timestamp:当前日志分段最大的时间戳
    relativeOffset:时间戳所对应的消息的相对偏移量

有了它们,就可以快速地通过offset值或时间戳定位到消息的具体位置了。并且由于索引文件的size都不大,因此很容易将它们做内存映射(mmap),存取效率很高。

总结

ClickHouse的索引字段是由使用者通过order by指定的,所以一般不可以太多,否则索引文件过于稀疏,查找起来效率不够理想,并且基数太大或者太小的列一般不建议作为索引,如果基数很大的列,建议放在order by的后面。