網站建設發(fā)展佛山百度seo代理
一、跳數索引?
影響ClickHouse查詢性能的因素很多。在大多數場景中,關鍵因素是ClickHouse在計算查詢WHERE子句條件時是否可以使用主鍵。因此,選擇適用于最常見查詢模式的主鍵對于表的設計至關重要。
然而,無論如何仔細地調優(yōu)主鍵,不可避免地會出現不能有效使用它的查詢用例。用戶通常依賴于ClickHouse獲得時間序列類型的數據,但他們通常希望根據其他業(yè)務維度(如客戶id、網站URL或產品編號)分析同一批數據。在這種情況下,查詢性能可能會相當差,因為應用WHERE子句條件可能需要對每個列值進行完整掃描。雖然ClickHouse在這些情況下仍然相對較快,但計算數百萬或數十億個單獨的值將導致“非索引”查詢的執(zhí)行速度比基于主鍵的查詢慢得多。
在傳統(tǒng)的關系數據庫中,解決這個問題的一種方法是將一個或多個“二級”索引附加到表上。這是一個b-樹結構,允許數據庫在O(log(n))時間內找到磁盤上所有匹配的行,而不是O(n)時間(一次表掃描),其中n是行數。但是,這種類型的二級索引不適用于ClickHouse(或其他面向列的數據庫),因為磁盤上沒有單獨的行可以添加到索引中。
相反,ClickHouse提供了一種不同類型的索引,在特定情況下可以顯著提高查詢速度。這些結構被標記為跳數索引,因為它們使ClickHouse能夠跳過保證沒有匹配值的數據塊。
二、基本操作?
用戶只能在MergeTree表引擎上使用數據跳數索引。每個跳數索引都有四個主要參數:
- 索引名稱。索引名用于在每個分區(qū)中創(chuàng)建索引文件。此外,在刪除或具體化索引時需要將其作為參數。
- 索引的表達式。索引表達式用于計算存儲在索引中的值集。它可以是列、簡單操作符、函數的子集的組合。
- 類型。索引的類型控制計算,該計算決定是否可以跳過讀取和計算每個索引塊。
- GRANULARITY。每個索引塊由顆粒(granule)組成。例如,如果主表索引粒度為8192行,GRANULARITY為4,則每個索引“塊”將為32768行。
當用戶創(chuàng)建數據跳數索引時,表的每個數據部分目錄中將有兩個額外的文件。
- skpidx{index_name}.idx:包含排序的表達式值。
- skpidx{index_name}.mrk2:包含關聯數據列文件中的相應偏移量。
如果在執(zhí)行查詢并讀取相關列文件時,WHERE子句過濾條件的某些部分與跳數索引表達式匹配,ClickHouse將使用索引文件數據來確定每個相關的數據塊是必須被處理還是可以被繞過(假設塊還沒有通過應用主鍵索引被排除)。這里用一個非常簡單的示例:考慮以下加載了可預測數據的表。
CREATE TABLE skip_table
(my_key UInt64,my_value UInt64
)
ENGINE MergeTree primary key my_key
SETTINGS index_granularity=8192;INSERT INTO skip_table SELECT number, intDiv(number,4096) FROM numbers(100000000);
當執(zhí)行一個不使用主鍵的簡單查詢時,將掃描my_value列所有的一億條記錄:
SELECT * FROM skip_table WHERE my_value IN (125, 700)┌─my_key─┬─my_value─┐
│ 512000 │ 125 │
│ 512001 │ 125 │
│ ... | ... |
└────────┴──────────┘8192 rows in set. Elapsed: 0.079 sec. Processed 100.00 million rows, 800.10 MB (1.26 billion rows/s., 10.10 GB/s.
增加一個基本的跳數索引:
ALTER TABLE skip_table ADD INDEX vix my_value TYPE set(100) GRANULARITY 2;
通常,跳數索引只應用于新插入的數據,所以僅僅添加索引不會影響上述查詢。
要使已經存在的數據生效,那執(zhí)行:
ALTER TABLE skip_table MATERIALIZE INDEX vix;
重跑SQL:
SELECT * FROM skip_table WHERE my_value IN (125, 700)┌─my_key─┬─my_value─┐
│ 512000 │ 125 │
│ 512001 │ 125 │
│ ... | ... |
└────────┴──────────┘8192 rows in set. Elapsed: 0.051 sec. Processed 32.77 thousand rows, 360.45 KB (643.75 thousand rows/s., 7.08 MB/s.)
這次沒有再去處理1億行800MB的數據,ClickHouse只讀取和分析32768行360KB的數據—4個granule的數據。
下圖是更直觀的展示,這就是如何讀取和選擇my_value為125的4096行,以及如何跳過以下行而不從磁盤讀取:
通過在執(zhí)行查詢時啟用跟蹤,用戶可以看到關于跳數索引使用情況的詳細信息。在clickhouse-client中設置send_logs_level:
SET send_logs_level='trace';
這將在嘗試調優(yōu)查詢SQL和表索引時提供有用的調試信息。上面的例子中,調試日志顯示跳數索引過濾了大部分granule,只讀取了兩個:
<Debug> default.skip_table (933d4b2c-8cea-4bf9-8c93-c56e900eefd1) (SelectExecutor): Index `vix` has dropped 6102/6104 granules.
三、跳數索引類型?
1、minmax?
這種輕量級索引類型不需要參數。它存儲每個塊的索引表達式的最小值和最大值(如果表達式是一個元組,它分別存儲元組元素的每個成員的值)。對于傾向于按值松散排序的列,這種類型非常理想。在查詢處理期間,這種索引類型的開銷通常是最小的。
這種類型的索引只適用于標量或元組表達式——索引永遠不適用于返回數組或map數據類型的表達式。
2、set?
這種輕量級索引類型接受單個參數max_size,即每個塊的值集(0允許無限數量的離散值)。這個集合包含塊中的所有值(如果值的數量超過max_size則為空)。這種索引類型適用于每組顆粒中基數較低(本質上是“聚集在一起”)但總體基數較高的列。
該索引的成本、性能和有效性取決于塊中的基數。如果每個塊包含大量惟一值,那么針對大型索引集計算查詢條件將非常昂貴,或者由于索引超過max_size而為空,因此索引將不應用。
3、Bloom Filter Types?
Bloom filter是一種數據結構,它允許對集合成員進行高效的是否存在測試,但代價是有輕微的誤報。在跳數索引的使用場景,假陽性不是一個大問題,因為惟一的問題只是讀取一些不必要的塊。潛在的假陽性意味著索引表達式應該為真,否則有效的數據可能會被跳過。
因為Bloom filter可以更有效地處理大量離散值的測試,所以它們可以適用于大量條件表達式判斷的場景。特別的是Bloom filter索引可以應用于數組,數組中的每個值都被測試,也可以應用于map,通過使用mapKeys或mapValues函數將鍵或值轉換為數組。
有三種基于Bloom過濾器的數據跳數索引類型:
-
基本的bloom_filter接受一個可選參數,該參數表示在0到1之間允許的“假陽性”率(如果未指定,則使用.025)。
-
更專業(yè)的tokenbf_v1。需要三個參數,用來優(yōu)化布隆過濾器:(1)過濾器的大小字節(jié)(大過濾器有更少的假陽性,有更高的存儲成本),(2)哈希函數的個數(更多的散列函數可以減少假陽性)。(3)布隆過濾器哈希函數的種子。有關這些參數如何影響布隆過濾器功能的更多細節(jié),請參閱?這里?。此索引僅適用于String、FixedString和Map類型的數據。輸入表達式被分割為由非字母數字字符分隔的字符序列。例如,列值
This is a candidate for a "full text" search
將被分割為This
?is
?a
?candidate
?for
?full
?text
?search
。它用于LIKE、EQUALS、in、hasToken()和類似的長字符串中單詞和其他值的搜索。例如,一種可能的用途是在非結構的應用程序日志行列中搜索少量的類名或行號。 -
更專業(yè)的ngrambf_v1。該索引的功能與tokenbf_v1相同。在Bloom filter設置之前需要一個額外的參數,即要索引的ngram的大小。一個ngram是長度為n的任何字符串,比如如果n是4,
A short string
會被分割為A sh`` sho
,?shor
,?hort
,?ort s
,?or st
,?r str
,?stri
,?trin
,?ring
。這個索引對于文本搜索也很有用,特別是沒有單詞間斷的語言,比如中文。
四、跳數索引函數?
跳數索引核心目的是限制流行查詢分析的數據量。鑒于ClickHouse數據的分析特性,這些查詢的模式在大多數情況下都包含函數表達式。因此,跳數索引必須與常用函數正確交互才能提高效率。這種情況可能發(fā)生在:
- 插入數據并將索引定義為一個函數表達式(表達式的結果存儲在索引文件中)或者
- 處理查詢,并將表達式應用于存儲的索引值,以確定是否排除數據塊。
每種類型的跳數索引支持的函數列表可以查看?這里?。通常,集合索引和基于Bloom filter的索引(另一種類型的集合索引)都是無序的,因此不能用于范圍。相反,最大最小值索引在范圍中工作得特別好,因為確定范圍是否相交非???。部分匹配函數LIKE、startsWith、endsWith和hasToken的有效性取決于使用的索引類型、索引表達式和數據的特定形狀。
五、跳數索引的配置?
有兩個可用的設置可應用于跳數索引。
- use_skip_indexes?(0或1,默認為1)。不是所有查詢都可以有效地使用跳過索引。如果一個特定的過濾條件可能包含很多顆粒,那么應用數據跳過索引將導致不必要的、有時甚至是非常大的成本。對于不太可能從任何跳過索引中獲益的查詢,將該值設置為0。
- force_data_skipping_indexes?(以逗號分隔的索引名列表)。此設置可用于防止某些類型的低效查詢。在某些情況下,除非使用跳過索引,否則查詢表的開銷太大,如果將此設置與一個或多個索引名一起使用,則對于任何沒有使用所列索引的查詢將返回一個異常。這將防止編寫糟糕的查詢消耗服務器資源。
六、最佳實踐?
跳數索引并不直觀,特別是對于來自RDMS領域并且習慣二級行索引或來自文檔存儲的反向索引的用戶來說。要獲得任何優(yōu)化,應用ClickHouse數據跳數索引必須避免足夠多的顆粒讀取,以抵消計算索引的成本。關鍵是,如果一個值在一個索引塊中只出現一次,就意味著整個塊必須讀入內存并計算,而索引開銷是不必要的。
考慮以下數據分布:
假設主鍵/順序是時間戳,并且在visitor_id上有一個索引??紤]下面的查詢:
SELECT timestamp, url FROM table WHERE visitor_id = 1001
對于這種數據分布,傳統(tǒng)的二級索引非常有利。不是讀取所有的32678行來查找具有請求的visitor_id的5行,而是二級索引只包含5行位置,并且只從磁盤讀取這5行。ClickHouse數據跳過索引的情況正好相反。無論跳轉索引的類型是什么,visitor_id列中的所有32678值都將被測試。
因此,試圖通過簡單地向鍵列添加索引來加速ClickHouse查詢的沖動通常是不正確的。只有在研究了其他替代方法之后,才應該使用此高級功能,例如修改主鍵(查看?如何選擇主鍵)、使用投影或使用實體化視圖。即使跳數索引是合適的,也經常需要對索引和表進行仔細的調優(yōu)。
在大多數情況下,一個有用的跳數索引需要主鍵和目標的非主列/表達式之間具有很強的相關性。如果沒有相關性(如上圖所示),那么在包含數千個值的塊中,至少有一行滿足過濾條件的可能性很高,并且只有幾個塊會被跳過。相反,如果主鍵的值范圍(如一天中的時間)與潛在索引列中的值強相關(如電視觀眾年齡),則最小值類型的索引可能是有益的。注意,在插入數據時,可以增加這種相關性,方法是在sort /ORDER by鍵中包含額外的列,或者以在插入時對與主鍵關聯的值進行分組的方式對插入進行批處理。例如,即使主鍵是一個包含大量站點事件的時間戳,特定site_id的所有事件也都可以被分組并由寫入進程插入到一起,這將導致許多只包含少量站點id的顆粒,因此當根據特定的site_id值搜索時,可以跳過許多塊。
跳數索引的另一個候選者是高基數表達式,其中任何一個值在數據中都相對稀疏。一個可能的例子是跟蹤API請求中的錯誤代碼的可觀察性平臺。某些錯誤代碼雖然在數據中很少出現,但對搜索來說可能特別重要。error_code列上的set skip索引將允許繞過絕大多數不包含錯誤的塊,從而顯著改善針對錯誤的查詢。
最后,關鍵的最佳實踐是測試、測試、再測試。同樣,與用于搜索文檔的b-樹二級索引或倒排索引不同,跳數索引行為是不容易預測的。將它們添加到表中會在數據攝取和查詢方面產生很大的成本,這些查詢由于各種原因不能從索引中受益。它們應該總是在真實世界的數據類型上進行測試,測試應該包括類型、粒度大小和其他參數的變化。測試通常會暴露僅僅通過思考不能發(fā)現的陷阱。