溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

MergeTree中clickhouse稀疏索引怎么用

發(fā)布時(shí)間:2021-12-10 11:39:02 來(lái)源:億速云 閱讀:296 作者:小新 欄目:大數(shù)據(jù)

這篇文章主要為大家展示了“MergeTree中clickhouse稀疏索引怎么用”,內(nèi)容簡(jiǎn)而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領(lǐng)大家一起研究并學(xué)習(xí)一下“MergeTree中clickhouse稀疏索引怎么用”這篇文章吧。

MergeTree存儲(chǔ)結(jié)構(gòu)邏輯示意圖

MergeTree中clickhouse稀疏索引怎么用

MergeTree表的存儲(chǔ)結(jié)構(gòu)中,每個(gè)數(shù)據(jù)分區(qū)相互獨(dú)立,邏輯上沒(méi)有關(guān)聯(lián)。單個(gè)數(shù)據(jù)分區(qū)內(nèi)部存在著多個(gè)MergeTree Data Part。這些Data Part一旦生成就是Immutable的狀態(tài),Data Part的生成和銷毀主要與寫入和異步Merge有關(guān)。MergeTree表的寫入鏈路是一個(gè)極端的batch load過(guò)程,Data Part不支持單條的append insert。每次batch insert都會(huì)生成一個(gè)新的MergeTree Data Part。如果用戶單次insert一條記錄,那就會(huì)為那一條記錄生成一個(gè)獨(dú)立的Data Part,這必然是無(wú)法接受的。一般我們使用MergeTree表引擎的時(shí)候,需要在客戶端做聚合進(jìn)行batch寫入。

1、概念

  • part:一次寫入生成的一個(gè)數(shù)據(jù)塊。

  • primary.idx文件:存儲(chǔ)了稀疏索引,一個(gè)part對(duì)應(yīng)一個(gè)稀疏索引。

mark numbervalue
0a
1a
2b
  • bin文件:真正存儲(chǔ)數(shù)據(jù)的文件,由1到多個(gè)壓縮數(shù)據(jù)組成。壓縮數(shù)據(jù)是最小存儲(chǔ)單位,由『頭文件』和『壓縮數(shù)據(jù)塊』組成。頭文件由壓縮算法、壓縮前的字節(jié)大小、壓縮后的字節(jié)大小三部分組成;壓縮數(shù)據(jù)塊嚴(yán)格限定在壓縮前64K~1M byte大小。(這個(gè)大小是ClickHouse認(rèn)為的壓縮與解壓性能消耗最小的大小)。即,一個(gè)壓縮數(shù)據(jù)塊由N個(gè)block組成,一個(gè)bin文件又由N個(gè)壓縮數(shù)據(jù)塊組成。

MergeTree中clickhouse稀疏索引怎么用

  • mrk文件:存儲(chǔ)了block在bin文件中哪個(gè)壓縮數(shù)據(jù)以及這個(gè)壓縮數(shù)據(jù)的數(shù)據(jù)塊中的起始偏移量。

索引中的mark index壓縮數(shù)據(jù)index在壓縮數(shù)據(jù)塊中起始的字節(jié)數(shù)(偏移量)
000
1012001
210

2、數(shù)據(jù)存儲(chǔ)

文件目錄

MergeTree中clickhouse稀疏索引怎么用

action_id.bin、avatar_id.bin等都是單個(gè)列按塊壓縮后的列存文件

  • 數(shù)據(jù)以壓縮數(shù)據(jù)為單位,存儲(chǔ)在bin文件中。

  • 壓縮數(shù)據(jù)對(duì)應(yīng)的壓縮數(shù)據(jù)塊,嚴(yán)格限定按照64K~1M byte的大小來(lái)進(jìn)行存儲(chǔ)。
    (1)如果一個(gè)block對(duì)應(yīng)的大小小于64K,則需要找下一個(gè)block來(lái)拼湊,直到拼湊出來(lái)的大小大于等于64K。
    (2)如果一個(gè)block的大小在64K到1M的范圍內(nèi),則直接生成1個(gè)壓縮數(shù)據(jù)塊。
    (3)如果一個(gè)block的大小大于了1M,則切割生成多個(gè)壓縮數(shù)據(jù)塊。

  • 一個(gè)part下不同的列分別存儲(chǔ),不同的列存儲(chǔ)的行數(shù)是一樣的。

Mark標(biāo)識(shí)文件:action_id.mrk2、avatar_id.mrk2等都是列存文件中的Mark標(biāo)記,Mark標(biāo)記和MergeTree列存中的兩個(gè)重要概念相關(guān):Granule和Block。

  • Granule是數(shù)據(jù)按行劃分時(shí)用到的邏輯概念。關(guān)于多少行是一個(gè)Granule這個(gè)問(wèn)題,在老版本中這是用參數(shù)index_granularity設(shè)定的一個(gè)常量,也就是每隔確定行就是一個(gè)Granule。在當(dāng)前版本中有另一個(gè)參數(shù)index_granularity_bytes會(huì)影響Granule的行數(shù),它的意義是讓每個(gè)Granule中所有列的sum size盡量不要超過(guò)設(shè)定值。老版本中的定長(zhǎng)Granule設(shè)定主要的問(wèn)題是MergeTree中的數(shù)據(jù)是按Granule粒度進(jìn)行索引的,這種粗糙的索引粒度在分析超級(jí)大寬表的場(chǎng)景中,從存儲(chǔ)讀取的data size會(huì)膨脹得非常厲害,需要用戶非常謹(jǐn)慎得設(shè)定參數(shù)。

  • Block是列存文件中的壓縮單元。每個(gè)列存文件的Block都會(huì)包含若干個(gè)Granule,具體多少個(gè)Granule是由參數(shù)min_compress_block_size控制,每次列的Block中寫完一個(gè)Granule的數(shù)據(jù)時(shí),它會(huì)檢查當(dāng)前Block Size有沒(méi)有達(dá)到設(shè)定值,如果達(dá)到則會(huì)把當(dāng)前Block進(jìn)行壓縮然后寫磁盤。

  • 從以上兩點(diǎn)可以看出MergeTree的Block既不是定data size也不是定行數(shù)的,Granule也不是一個(gè)定長(zhǎng)的邏輯概念。所以我們需要額外信息快速找到某一個(gè)Granule。這就是Mark標(biāo)識(shí)文件的作用,它記錄了每個(gè)Granule的行數(shù),以及它所在的Block在列存壓縮文件中的偏移,同時(shí)還有Granule在解壓后的Block中的偏移位置。

主鍵索引:primary.idx是表的主鍵索引。ClickHouse對(duì)主鍵索引的定義和傳統(tǒng)數(shù)據(jù)庫(kù)的定義稍有不同,它的主鍵索引沒(méi)用主鍵去重的含義,但仍然有快速查找主鍵行的能力。ClickHouse的主鍵索引存儲(chǔ)的是每一個(gè)Granule中起始行的主鍵值,而MergeTree存儲(chǔ)中的數(shù)據(jù)是按照主鍵嚴(yán)格排序的。所以當(dāng)查詢給定主鍵條件時(shí),我們可以根據(jù)主鍵索引確定數(shù)據(jù)可能存在的 ,再結(jié)合上面介紹的Mark標(biāo)識(shí),我們可以進(jìn)一步確定數(shù)據(jù)在列存文件中的位置區(qū)間。ClickHoue的主鍵索引是一種在索引構(gòu)建成本和索引效率上相對(duì)平衡的粗糙索引。MergeTree的主鍵序列默認(rèn)是和Order By序列保存一致的,但是用戶可以把主鍵序列定義成Order By序列的部分前綴。

分區(qū)鍵索引:minmax_time.idx、minmax_region_name.idx是表的分區(qū)鍵索引。MergeTree存儲(chǔ)會(huì)把統(tǒng)計(jì)每個(gè)Data Part中分區(qū)鍵的最大值和最小值,當(dāng)用戶查詢中包含分區(qū)鍵條件時(shí),就可以直接排除掉不相關(guān)的Data Part,這是一種OLAP場(chǎng)景下常用的分區(qū)裁剪技術(shù)。

Skipping索引:skp_idx_avatar_id_minmax.idx是用戶在avatar_id列上定義的MinMax索引。Merge Tree中 的Skipping Index是一類局部聚合的粗糙索引。用戶在定義skipping index的時(shí)候需要設(shè)定granularity參數(shù),這里的granularity參數(shù)指定的是在多少個(gè)Granule的數(shù)據(jù)上做聚合生成索引信息。用戶還需要設(shè)定索引對(duì)應(yīng)的聚合函數(shù),常用的有minmax、set、bloom_filter、ngrambf_v1等,聚合函數(shù)會(huì)統(tǒng)計(jì)連續(xù)若干個(gè)Granule中的列值生成索引信息。Skipping索引的思想和主鍵索引是類似的,因?yàn)閿?shù)據(jù)是按主鍵排序的,主鍵索引統(tǒng)計(jì)的其實(shí)就是每個(gè)Granule粒度的主鍵序列MinMax值,而Skipping索引提供的聚合函數(shù)種類更加豐富,是主鍵索引的一種補(bǔ)充能力。另外這兩種索引都是需要用戶在理解索引原理的基礎(chǔ)上貼合自己的業(yè)務(wù)場(chǎng)景來(lái)進(jìn)行設(shè)計(jì)的。

3、檢索過(guò)程

MergeTree存儲(chǔ)在收到一個(gè)select查詢時(shí)會(huì)先抽取出查詢中的分區(qū)鍵和主鍵條件的KeyCondition,KeyCondition類上實(shí)現(xiàn)了以下三個(gè)方法,用于判斷過(guò)濾條件可能滿足的Mark Range。上面講過(guò)MergeTree Data Part中的列存數(shù)據(jù)是以Granule為粒度被Mark標(biāo)識(shí)數(shù)組索引起來(lái)的,而Mark Range就表示Mark標(biāo)識(shí)數(shù)組里滿足查詢條件的下標(biāo)區(qū)間。

索引檢索的過(guò)程中首先會(huì)用分區(qū)鍵KeyCondition裁剪掉不相關(guān)的數(shù)據(jù)分區(qū),然后用主鍵索引挑選出粗糙的Mark Range,最后再用Skipping Index過(guò)濾主鍵索引產(chǎn)生的Mark Range。用主鍵索引挑選出粗糙的Mark Range的算法是一個(gè)不斷分裂Mark Range的過(guò)程,返回結(jié)果是一個(gè)Mark Range的集合。起始的Mark Range是覆蓋整個(gè)MergeTree Data Part區(qū)間的,每次分裂都會(huì)把上次分裂后的Mark Range取出來(lái)按一定粒度步長(zhǎng)分裂成更細(xì)粒度的Mark Range,然后排除掉分裂結(jié)果中一定不滿足條件的Mark Range,最后Mark Range到一定粒度時(shí)停止分裂。這是一個(gè)簡(jiǎn)單高效的粗糙過(guò)濾算法。

使用Skipping Index過(guò)濾主鍵索引返回的Mark Range之前,需要構(gòu)造出每個(gè)Skipping Index的IndexCondition,不同的Skipping Index聚合函數(shù)有不同的IndexCondition實(shí)現(xiàn),但判斷Mark Range是否滿足條件的接口和KeyCondition是類似的。

數(shù)據(jù)Sampling

經(jīng)過(guò)上一小節(jié)的索引過(guò)濾之后,我們已經(jīng)得到了需要掃描的Mark Range集合,接下來(lái)就應(yīng)該是數(shù)據(jù)掃描部分了。這一小節(jié)插入簡(jiǎn)單講一下MergeTree里的數(shù)據(jù)Sampling是如何實(shí)現(xiàn)的。它并不是在數(shù)據(jù)掃描過(guò)程中實(shí)現(xiàn)的,而是在索引檢索的過(guò)程中就已經(jīng)完成,這種做法是為了極致的sample效率。用戶在建表的時(shí)候可以指定主鍵中的某個(gè)列或者表達(dá)式作為Sampling鍵,ClickHouse在這里用了簡(jiǎn)單粗暴的做法:Sampling鍵的值必須是數(shù)值類型的,并且系統(tǒng)假定它的值是隨機(jī)均勻分布的一個(gè)狀態(tài)。如果Sampling鍵的值類型是Uint32,當(dāng)我們?cè)O(shè)定sample比率是0.1的時(shí)候,索引檢索過(guò)程中會(huì)把sample轉(zhuǎn)換成一個(gè)filter條件:Sampling鍵的值 < Uint32::max * 0.1。用戶在使用Sampling功能時(shí)必須清楚這個(gè)細(xì)節(jié),不然容易出現(xiàn)采樣偏差。一般我們推薦Sampling鍵是列值加一個(gè)Hash函數(shù)進(jìn)行隨機(jī)打散。

數(shù)據(jù)掃描

MergeTree的數(shù)據(jù)掃描部分提供了三種不同的模式:

  • Final模式:該模式對(duì)CollapsingMergeTree、SummingMergeTree等表引擎提供一個(gè)最終Merge后的數(shù)據(jù)視圖。前文已經(jīng)提到過(guò)MergeTree基礎(chǔ)上的高級(jí)MergeTree表引擎都是對(duì)MergeTree Data Part采用了特定的Merge邏輯。它帶來(lái)的問(wèn)題是由于MergeTree Data Part是異步Merge的過(guò)程,在沒(méi)有最終Merge成一個(gè)Data Part的情況下,用戶無(wú)法看到最終的數(shù)據(jù)結(jié)果。所以ClickHouse在查詢是提供了一個(gè)final模式,它會(huì)在各個(gè)Data Part的多條BlockInputStream基礎(chǔ)上套上一些高級(jí)的Merge Stream,例如DistinctSortedBlockInputStream、SummingSortedBlockInputStream等,這部分邏輯和異步Merge時(shí)的邏輯保持一致,這樣用戶就可以提前看到“最終”的數(shù)據(jù)結(jié)果了。

  • Sorted模式:sort模式可以認(rèn)為是一種order by下推存儲(chǔ)的查詢加速優(yōu)化手段。因?yàn)槊總€(gè)MergeTree Data Part內(nèi)部的數(shù)據(jù)是有序的,所以當(dāng)用戶查詢中包括排序鍵order by條件時(shí)只需要在各個(gè)Data Part的BlockInputStream上套一個(gè)做數(shù)據(jù)有序歸并的InputStream就可以實(shí)現(xiàn)全局有序的能力。

  • Normal模式:這是基礎(chǔ)MergeTree表最常用的數(shù)據(jù)掃描模式,多個(gè)Data Part之間進(jìn)行并行數(shù)據(jù)掃描,對(duì)于單查詢可以達(dá)到非常高吞吐的數(shù)據(jù)讀取。

接下來(lái)展開(kāi)介紹下Normal模式中幾個(gè)關(guān)鍵的性能優(yōu)化點(diǎn):

  • 并行掃描:傳統(tǒng)的計(jì)算引擎在數(shù)據(jù)掃描部分的并發(fā)度大多和存儲(chǔ)文件數(shù)綁定在一起,所以MergeTree Data Part并行掃描是一個(gè)基礎(chǔ)能力。但是MergeTree的存儲(chǔ)結(jié)構(gòu)要求數(shù)據(jù)不斷mege,最終合并成一個(gè)Data Part,這樣對(duì)索引和數(shù)據(jù)壓縮才是最高效的。所以ClickHouse在MergeTree Data Part并行的基礎(chǔ)上還增加了Mark Range并行。用戶可以任意設(shè)定數(shù)據(jù)掃描過(guò)程中的并行度,每個(gè)掃描線程分配到的是Mark Range In Data Part粒度的任務(wù),同時(shí)多個(gè)掃描線程之間還共享了Mark Range Task Pool,這樣可以避免在存儲(chǔ)掃描中的長(zhǎng)尾問(wèn)題。

  • 數(shù)據(jù)Cache:MergeTree的查詢鏈路中涉及到的數(shù)據(jù)有不同級(jí)別的緩存設(shè)計(jì)。主鍵索引和分區(qū)鍵索引在load Data Part的過(guò)程中被加載到內(nèi)存,Mark文件和列存文件有對(duì)應(yīng)的MarkCache和UncompressedCache,MarkCache直接緩存了Mark文件中的binary內(nèi)容,而UncompressedCache中緩存的是解壓后的Block數(shù)據(jù)。

  • SIMD反序列化:部分列類型的反序列化過(guò)程中采用了手寫的sse指令加速,在數(shù)據(jù)命中UncompressedCache的情況下會(huì)有一些效果。

  • PreWhere過(guò)濾:ClickHouse的語(yǔ)法支持了額外的PreWhere過(guò)濾條件,它會(huì)先于Where條件進(jìn)行判斷。當(dāng)用戶在sql的filter條件中加上PreWhere過(guò)濾條件時(shí),存儲(chǔ)掃描會(huì)分兩階段進(jìn)行,先讀取PreWhere條件中依賴的列值,然后計(jì)算每一行是否符合條件。相當(dāng)于在Mark Range的基礎(chǔ)上進(jìn)一步縮小掃描范圍,PreWhere列掃描計(jì)算過(guò)后,ClickHouse會(huì)調(diào)整每個(gè)Mark對(duì)應(yīng)的Granule中具體要掃描的行數(shù),相當(dāng)于可以丟棄Granule頭尾的一部分行。

以上是“MergeTree中clickhouse稀疏索引怎么用”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI