天天看點

一種神奇的索引--ClickHouse中的跳數索引

前言

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,這個索引對于文本搜尋也很有用。

資料是怎麼被跳過的?

跳數索引的核心是跳過那些一定不會被命中的資料,進而隻在少量的顆粒中進行查詢,提升查詢速度。

首先增加跳數索引後,會增加兩個實體檔案

一種神奇的索引--ClickHouse中的跳數索引

skp_idx_{index_name}.idx:具體的索引檔案。

skp_idx_{index_name}.mrk2:存儲顆粒資訊。

借用官網的一張圖:

一種神奇的索引--ClickHouse中的跳數索引

在上圖中,跳數索引設定 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 異常:

一種神奇的索引--ClickHouse中的跳數索引