在 Doris 的存儲引擎規則: 表的數據是以分區為單位存儲的,不指定分區創建時,預設就一個分區. 用戶數據首先被劃分成若幹個分區(Partition),劃分的規則通常是按照用戶指定的分區列進行範圍劃分,比如按時間劃分。 在每個分區內,數據被進一步的按照Hash的方式分桶,分桶的規則是要找用戶指定的 ...
1、minmax
下麵是為url建立最大最小值的跳數索引 ALTER TABLE hits_UserID_URL ADD INDEX url_skipping_index URL TYPE minmax GRANULARITY 4; ALTER TABLE hits_UserID_URL MATERIALIZE INDEX url_skipping_index;
每4個顆粒創建用一個索引,這個索引存儲了url的最大值和最小值,在查詢執行過程中,ClickHouse 可以在不掃描列的情況下快速檢查列值是否超出範圍,並跳過不滿足最大最小值的顆粒塊。有點類似clickhouse建立多個主鍵。所以當列的值隨排序順序緩慢變化時,它的效果最好。
官網說可以使用優化
- Creating a second table with a different primary key.用不同的主鍵建立多個表,這樣子的話那就考慮兩張表的數據同步問題,如果這樣實現可以試下物化視圖
-
- Adding a projection to our existing table.建立投影(還不會,不知道怎麼評價)
這三種我都沒用過,感覺有點麻煩
鍵列之間的基數差越大,這些列在鍵中的順序就越重要。
2、set:
ALTER TABLE skip_table ADD INDEX vix my_value TYPE set(100) GRANULARITY 2;
這種輕量級索引類型接受單個參數max_size,這種索引會將指定顆粒中的所有不同值存儲起來,如果不同值數量超過了max_size,該索引就不生效。ClickHouse 在使用 where 條件查詢時,如果遇到了 set 類型的跳數索引,則會檢查 where 條件中的值是否在 set 集合中,如果不在就跳過這些顆粒。 適合聚集那種的,特別是枚舉的,例如省份 3、布隆過濾器
布隆過濾器tokenbf_v1:
這是按照token進行分詞的布隆過濾器索引,它會將長文本中的單詞按照非字母數字字元(如空格、數字、漢字)進行分詞,每個分詞就是一個token,然後這個token映射到布隆過濾器的bitmap中。
舉個例子, https://clickhouse.com/docs/en/engines/table-engines/mergetree-family/mergetree#table_engine-mergetree-data_skipping-indexes
這個字元串,經過分詞後,會變成如下的token
['https', 'clickhouse', 'com', 'docs', 'en', 'engines', 'table', 'engines', 'mergetree', 'family', 'mergetree', 'table', 'engine', 'data', 'skipping', 'indexes']
從分詞中可以看出,比較死板,如果查詢條件中有特殊字元,索引就會失效
無法支持中文、數字
布隆過濾器ngrambf_v1:
ngrambf_v1和tokenbf_v1的區別在於不是按照token分詞而是按照長度分詞
舉個例子:Hell Flink;按照n=4分詞得到的是[Hell, Fli]
優點是:解決了特殊字元以及漢字的問題
缺點是:長度一旦確定就不好修改了,查詢的關鍵詞小於n,索引就不會生效。ngrambf_v1
比較適合提前知道查詢的sql,針對性設置n的值。如果n設置過小會出現假陽性過高的問題。
所以在使用跳數索引的時候一定要確認自己的索引生效,不然會有出現負優化。
CREATE TABLE ck_log_test( `logType` String, `@timestamp` DateTime64(3), `ip` String, `filePath` String, `cloudId` UInt64, `time` DateTime64(3), `id` String, `tms` UInt64, `rowNumber` UInt64, `value` String, `@storageTime` DateTime64(3), `fd` UInt64 ) ENGINE = ReplicatedMergeTree() PARTITION BY toYYYYMMDD(`@timestamp`) ORDER BY (`@timestamp`); -- 建立索引 ALTER TABLE ck_log_test ADD INDEX idx_ngram3 value TYPE ngrambf_v1(48, 307200, 2, 0) GRANULARITY 1; ALTER TABLE ck_log_test MATERIALIZE INDEX idx_ngram3; EXPLAIN indexes = 1 SELECT count() FROM ck_log_test WHERE (value LIKE '%gfdsamnbvcxz-asdfghjkl-poiuytrewqlkjh-qwertyuiop%') AND (value LIKE '%[INFO]%'); ┌─explain───────────────────────────────────────────┐ │ Expression ((Projection + Before ORDER BY)) │ │ Aggregating │ │ Expression (Before GROUP BY) │ │ Filter (WHERE) │ │ ReadFromMergeTree (ck_log_test) │ │ Indexes: │ │ MinMax │ │ Condition: true │ │ Parts: 9/9 │ │ Granules: 12929/12929 │ │ Partition │ │ Condition: true │ │ Parts: 9/9 │ │ Granules: 12929/12929 │ │ PrimaryKey │ │ Condition: true │ │ Parts: 9/9 │ │ Granules: 12929/12929 │ │ Skip │ │ Name: idx_ngram3 │ │ Description: ngrambf_v1 GRANULARITY 1 │ │ Parts: 9/9 │ │ Granules: 1253/12929 │ └───────────────────────────────────────────────────┘
分析執行計劃
首先是minmax 這個是在見表時的orderby 的欄位進行篩選的,因為沒有帶任何@timestamp所以還是完整的12929個塊
接著是Partition 數據都在一個分區,所以過濾不掉
接著是primaryKey,但是在建表中沒有顯式指定主鍵索引,所以用了order by 的欄位作為主鍵索引,也沒過濾掉
重頭戲來了:Skip 跳數索引,索引idx_ngram3是我們建立的,從12929個塊過濾剩下1253個塊。說明索引建立是生效的,也沒起到作用
而且加索引是加快了讀的速率,但是卻影響了寫的效率。讀和寫的一個效率的綜合考慮去建立索引才是一個比較合適的方案。顧此失彼不是一個完美的選擇。