前言
ClickHouse中除了主键索引之外还有一种叫做跳数索引(Skipping index)的索引类型,就和这种索引的名称一样,跳数索引主要做的事情就是在查询时将非必要的数据过滤掉,只查询有用的数据。
介绍
跳数索引只能在MergeTree引擎上使用,跳数索引中主要有四个参数:
- 索引名称。索引名用于在每个分区中创建索引文件。
- 索引的表达式。索引表达式用于计算存储在索引中的值集。它可以是列、简单操作符、函数的子集的组合。
- 类型。索引的类型。
- GRANULARITY。每个索引块由颗粒(granule)组成。例如,如果主表索引粒度为8192行,GRANULARITY为4,则每个索引“块”将为32768行。
跳数索引可以在创建表时添加:
sql复制代码create table ck_test.test_skip_index
(
id UInt32,
org_id String,
user_id Int32,
name String,
l_date Date,
index idx_org_id org_id TYPE set(100) GRANULARITY 2
)
ENGINE = MergeTree
PARTITION BY toYYYYMM(l_date)
PRIMARY KEY(id)
ORDER BY id;
也可以在建完表之后添加:
sql复制代码ALTER TABLE ck_test.test_skip_index ADD INDEX idx_user_id user_id TYPE set(100) GRANULARITY 2;
跳数索引只应用于新插入的数据,所以仅仅添加索引不会影响已经有的数据,要使已经有的数据也生效,需要重新执行下面的命令:
sql复制代码ALTER TABLE ck_test.test_skip_index MATERIALIZE INDEX idx_user_id;
跳数索引中,不仅可以为某个列常见索引,也能为某个表达式创建索引
sql复制代码ALTER TABLE ck_test.test_skip_index ADD INDEX idx_calculate (id*user_id) TYPE minmax GRANULARITY 2;
这一类索引可以提高特定查询的性能,就比如刚好要对 id*user_id 作为条件进行查询:
sql复制代码select * from ck_test.test_skip_index where id*user_id=10;
索引类型
minmax
Minmax 是最简单的一种。它存储特定列(或表达式)的最小值和最大值,在查询执行过程中,ClickHouse 可以在不扫描列的情况下快速检查列值是否超出范围,并跳过不满足最大最小值的颗粒块。当列的值随排序顺序缓慢变化时,它的效果最好。
set
这种轻量级索引类型接受单个参数max_size,这种索引会将指定颗粒中的所有不同值存储起来,如果不同值数量超过了max_size,该索引就不生效。
ClickHouse 在使用 where 条件查询时,如果遇到了 set 类型的跳数索引,则会检查 where 条件中的值是否在 set 集合中,如果不在就跳过这些颗粒。
set 类型的索引很适合那种有大量重复值的列,比如枚举列(省市等)。
布隆过滤器
很多人刚开始听说布隆过滤器应该是在 redis 中,一个数据如果在布隆过滤器中返回不存在,那么这个数一定不存在;如果一个数在布隆过滤器中返回存在,那么有一定的可能这个数据不存在。布隆过滤器可以很快地过滤掉那些一定不存在的数据。ClickHouse提供了三种布隆过滤器过滤方法。
bloom_filter
sql复制代码ALTER TABLE ck_test.test_skip_index ADD INDEX idx_user_bf user_id TYPE bloom_filter(0.25) GRANULARITY 2;
基本的bloom_filter接受一个可选参数,该参数表示在0到1之间允许的“假阳性”率(如果未指定,则使用.025)。
tokenbf_v1
sql复制代码ALTER TABLE ck_test.test_skip_index ADD INDEX idx_name_token_bf name TYPE tokenbf_v1(256, 2, 0) GRANULARITY 2;
相比基本的 bloom_filter 更加专业,需要三个参数,用来优化布隆过滤器
- size_of_bloom_filter_in_bytes – 布隆过滤器大小,字节为单位。(因为压缩得好,可以指定比较大的值,如 256 或 512)。
- number_of_hash_functions – 布隆过滤器中使用的哈希函数的个数。
- random_seed – 哈希函数的随机种子。
tokenbf_v1 主要用于字符串的过滤,比如:This is a candidate for a "full text" search将被分割为This | is | a | candidate | for | full | text | search 存储,因此更适合 LIKE、EQUALS、in 等操作。
ngrambf_v1
sql复制代码ALTER TABLE ck_test.test_skip_index ADD INDEX idx_name_ngram_bf name TYPE ngrambf_v1(4, 256, 2, 0) GRANULARITY 2;
该索引的功能与tokenbf_v1相同。在Bloom filter设置之前需要一个额外的参数,即要索引的ngram的大小,一个ngram是长度为n的任何字符串。
A short string会被分割为A sho, shor, hort, ort s, or st, r str, stri, trin, ring,这个索引对于文本搜索也很有用。
数据是怎么被跳过的?
跳数索引的核心是跳过那些一定不会被命中的数据,从而只在少量的颗粒中进行查询,提升查询速度。
首先增加跳数索引后,会增加两个物理文件
skp_idx_{index_name}.idx:具体的索引文件。
skp_idx_{index_name}.mrk2:存储颗粒信息。
借用官网的一张图:
在上图中,跳数索引设置 GRANULARITY 为 2,想要查询 my_value 等于 125 的数据。因为第一个跳数索引块的两个颗粒中包含了 125 这个值,所以这个索引块没有被跳过。第二个跳数索引块中的颗粒没有包含 125 这个值,所以这个索引块就被跳过了。
针对跳数索引的配置
use_skip_indexes
use_skip_indexes 可以设置为 0 或者 1,默认是 1,如果想要在某一次查询中不使用跳数索引,则可以在查询时将该参数设置为 0。
sql复制代码SELECT * FROM ck_test.test_skip_index SETTINGS use_skip_indexes=0;
force_data_skipping_indices
如果将此设置与一个或多个索引名一起使用,则对于任何没有使用所列索引的查询将返回一个异常,force_data_skipping_indices 后面的索引以英文逗号分隔
sql复制代码SELECT * FROM ck_test.test_skip_index where name = '123' SETTINGS force_data_skipping_indices = 'idx_name_token_bf';
上面的这段SQL因为走了跳数索引所以不会抛出异常。
sql复制代码SELECT * FROM ck_test.test_skip_index SETTINGS force_data_skipping_indices = 'idx_name_token_bf';
这段 SQL 没有配 Where 条件,一定不会走跳数索引,就会抛出一个 INDEX_NOT_USED 异常: