ClickHouse Skip Index 初步解析

您所在的位置:网站首页 原神魈荧图 ClickHouse Skip Index 初步解析

ClickHouse Skip Index 初步解析

#ClickHouse Skip Index 初步解析| 来源: 网络整理| 查看: 265

1 前言

ClickHouse 是一个列式存储OLAP数据库,当(默认)使用MergeTree系列存储引擎时,列数据在磁盘中按主键顺序存储,且数据库对数据的操纵以granule即颗粒为单位,每个granule 包含若干条记录,默认为8192。 granule是流进ClickHouse进行数据处理的最小的不可分割数据集。

2 稀疏索引

ClickHouse使用的索引,不像常见的如MySQL等数据库,索引每一列数据。ClickHouse设计之初是为了处理PetaBytes级别的数据,于此,索引的占用空间不能太大,便于将索引载入主存中。

ClickHouse索引以granule为单位,每个索引记录称作一个mark,对于如下表:

CREATE TABLE hits_UserID_URL ( `UserID` UInt32, `URL` String, `EventTime` DateTime ) ENGINE = MergeTree PRIMARY KEY (UserID, URL) ORDER BY (UserID, URL, EventTime) SETTINGS index_granularity = 8192, index_granularity_bytes = 0;

主键索引结构如图:

每一个mark记录了所指向granule的第一条记录的主键所含列的值,这种索引方式即Sparse Index稀疏索引。

3 跳数索引

在ClickHouse中筛选非主键列数据进行分析,也是一个常用的使用场景。由于数据是以主键为序存储的,想要获取想要的非主键列数据,数据库需要遍历所有数据才能获取到想要的数据——如果只有主键索引。

Skip Index的作用类似于传统数据库的二级索引,加速对使用非主键列数据条件进行的筛选,但又不同于传统的二级索引。

传统的B-Tree结构二级索引,可以在O(log2n)时间复杂下快速定位到对应的单个索引记录,而后进行回表操作,获取想要的数据。索引记录和元组是一一对应的。

而Skip Index则根据建立索引时设定的granularity值对应若干个granule——ClickHouse是以granule为单位对数据进行IO的,如果单个granule对应8192单位数据,则每次IO都会读取8192个值,而不是想要的具体某一个值到内存中。

创建Skip Index的语句如下:

#set(n),n代表set最多记录的值数量 ALTER TABLE skip_table ADD INDEX vix my_value TYPE set(100) GRANULARITY 2; #如果需要让索引对建立索引之前的数据生效,需要执行如下sql ALTER TABLE skip_table MATERIALIZE INDEX vix; #或者创建表的时候就创建对应索引 CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster] ( ... INDEX index_name expr TYPE type(...) [GRANULARITY value], ... ) ENGINE = MergeTree()

大致结构如下:

图中涉及到4个granule,分别对应8192行数据,在此之上建立了set类型的Skip Index,每个索引记录对应了2个granule也就是它的粒度为2,记录了对应granule所包含的唯一值,可以快速过滤掉不含有所需值的granule。

3.1 跳数索引类型minmax

记录对应区域内的表达式/列的最小最大值,需要表达式或列的值可以比较。适用于数据至少是按索引列松散有序的情况。

set

记录了区域内所有的唯一值。适用于索引列有大量重复值的情况。

bloomfilter

每个区域有一个布隆过滤器,可以快速判断对应值是否存在于对应索引,通过计算对应值的hash。适用于数据较为离散的情况,否则大量的假阳性会让索引大大降低效率。

4 跳数索引更新机制

以minmax索引为例,更新操作位于update函数:

void MergeTreeIndexAggregatorMinMax::update(const Block & block, size_t * pos, size_t limit) { if (*pos >= block.rows()) throw Exception( "The provided position is not less than the number of block rows. Position: " + toString(*pos) + ", Block rows: " + toString(block.rows()) + ".", ErrorCodes::LOGICAL_ERROR); ​ //每次读取limit行,除非剩余行数小于limit size_t rows_read = std::min(limit, block.rows() - *pos); ​ FieldRef field_min; FieldRef field_max; //逐列处理结果集内的数据 for (size_t i = 0; i cut(*pos, rows_read); if (const auto * column_nullable = typeid_cast(column.get())) column_nullable->getExtremesNullLast(field_min, field_max); else column->getExtremes(field_min, field_max); ​ if (hyperrectangle.size()


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3