ClickHouse 数据存储原理:MergeTree引擎
文章目錄
- MergeTree引擎
- 存儲(chǔ)結(jié)構(gòu)
- 一級(jí)索引
- 稀疏索引
- 索引粒度index_granularity
- 索引的查詢過(guò)程
- 聯(lián)合主鍵
- 二級(jí)索引
- 跳數(shù)索引
- granularity
- 數(shù)據(jù)標(biāo)記
- 生成規(guī)則
- 工作方式
- 數(shù)據(jù)標(biāo)記與壓縮數(shù)據(jù)塊的對(duì)應(yīng)關(guān)系
- 工作流程
- 存儲(chǔ)流程
- 查詢流程
MergeTree引擎
存儲(chǔ)結(jié)構(gòu)
MergeTree的存儲(chǔ)結(jié)構(gòu)- partition:分區(qū)目錄,余下各類數(shù)據(jù)文件(primary.idx、[Column].mrk、[Column]. bin等)都是以分區(qū)目錄的形式被組織存放的,屬于相同分區(qū)的數(shù)據(jù),最終會(huì)被合并到同一個(gè)分區(qū)目錄,而不同分區(qū)的數(shù)據(jù),永遠(yuǎn)不會(huì)被合并在一起。
- checksums:校驗(yàn)文件,使用二進(jìn)制格式存儲(chǔ)。它保存了余下各類文件(primary. idx、count.txt等)的size大小及size的哈希值,用于快速校驗(yàn)文件的完整性和正確性。
- columns.txt:列信息文件,使用明文格式存儲(chǔ),用于保存此數(shù)據(jù)分區(qū)下的列字段信息。
- count.txt:計(jì)數(shù)文件,使用明文格式存儲(chǔ),用于記錄當(dāng)前數(shù)據(jù)分區(qū)目錄下數(shù)據(jù)的總行數(shù)。
- primary.idx:一級(jí)索引文件,使用二進(jìn)制格式存儲(chǔ)。用于存放稀疏索引,一張MergeTree表只能聲明一次一級(jí)索引。借助稀疏索引,在數(shù)據(jù)查詢的時(shí)能夠排除主鍵條件范圍之外的數(shù)據(jù)文件,從而有效減少數(shù)據(jù)掃描范圍,加速查詢速度。
- [Column].bin:數(shù)據(jù)文件,使用壓縮格式存儲(chǔ),用于存儲(chǔ)某一列的數(shù)據(jù)。由于MergeTree采用列式存儲(chǔ),所以每一個(gè)列字段都擁有獨(dú)立的.bin數(shù)據(jù)文件,并以列字段名稱命名。
- [Column].mrk:使用二進(jìn)制格式存儲(chǔ)。標(biāo)記文件中保存了.bin文件中數(shù)據(jù)的偏移量信息。標(biāo)記文件與稀疏索引對(duì)齊,又與.bin文件一一對(duì)應(yīng),所以MergeTree通過(guò)標(biāo)記文件建立了primary.idx稀疏索引與.bin數(shù)據(jù)文件之間的映射關(guān)系。即首先通過(guò)稀疏索引(primary.idx)找到對(duì)應(yīng)數(shù)據(jù)的偏移量信息(.mrk),再通過(guò)偏移量直接從.bin文件中讀取數(shù)據(jù)。由于.mrk標(biāo)記文件與.bin文件一一對(duì)應(yīng),所以MergeTree中的每個(gè)列字段都會(huì)擁有與其對(duì)應(yīng)的.mrk標(biāo)記文件
- [Column].mrk2:如果使用了自適應(yīng)大小的索引間隔,則標(biāo)記文件會(huì)以.mrk2命名。它的工作原理和作用與.mrk標(biāo)記文件相同。
- partition.dat與minmax_[Column].idx:如果使用了分區(qū)鍵,例如PARTITION BY EventTime,則會(huì)額外生成partition.dat與minmax索引文件,它們均使用二進(jìn)制格式存儲(chǔ)。partition.dat用于保存當(dāng)前分區(qū)下分區(qū)表達(dá)式最終生成的值;而minmax索引用于記錄當(dāng)前分區(qū)下分區(qū)字段對(duì)應(yīng)原始數(shù)據(jù)的最小和最大值。
- skp_idx_[Column].idx與skp_idx_[Column].mrk:如果在建表語(yǔ)句中聲明了二級(jí)索引,則會(huì)額外生成相應(yīng)的二級(jí)索引與標(biāo)記文件,它們同樣也使用二進(jìn)制存儲(chǔ)。二級(jí)索引在ClickHouse中又稱跳數(shù)索引。
一級(jí)索引
稀疏索引
當(dāng)我們定義主鍵之后,MergeTree會(huì)依據(jù)index_granularity間隔(默認(rèn)8192行),為數(shù)據(jù)表生成一級(jí)索引并保存至primary.idx文件內(nèi),索引數(shù)據(jù)按照主鍵排序。相比使用主鍵定義,更為常見(jiàn)的簡(jiǎn)化形式是通過(guò)ORDER BY指代主鍵。在此種情形下,主鍵與ORDER BY定義相同,所以索引(primary.idx)和數(shù)據(jù)(.bin)會(huì)按照完全相同的規(guī)則排序。
一級(jí)索引底層采用了稀疏索引來(lái)實(shí)現(xiàn),從下圖我們可以看出它和稠密索引的區(qū)別。
稀疏索引與稠密索引的對(duì)比對(duì)于稠密索引而言,每一行索引標(biāo)記都會(huì)對(duì)應(yīng)到具體的一行記錄上。而在稀疏索引中,每一行索引標(biāo)記對(duì)應(yīng)的一大段數(shù)據(jù),而不是具體的一行(他們之間的區(qū)別就有點(diǎn)類似mysql中innodb的聚集索引與非聚集索引)。
稀疏索引的優(yōu)勢(shì)是顯而易見(jiàn)的,它只需要使用少量的索引標(biāo)記就能夠記錄大量數(shù)據(jù)的區(qū)間位置信息,并且數(shù)據(jù)量越大優(yōu)勢(shì)愈發(fā)明顯。例如我們使用默認(rèn)的索引粒度(8192)時(shí),MergeTree只需要12208行索引標(biāo)記就能為1億行數(shù)據(jù)記錄提供索引。由于稀疏索引占用空間小,所以primary.idx內(nèi)的索引數(shù)據(jù)能夠常駐內(nèi)存,取用速度自然極快。
索引粒度index_granularity
索引粒度就如同標(biāo)尺一般,會(huì)丈量整個(gè)數(shù)據(jù)的長(zhǎng)度,并依照刻度對(duì)數(shù)據(jù)進(jìn)行標(biāo)注,最終將數(shù)據(jù)標(biāo)記成多個(gè)間隔的小段。數(shù)據(jù)以index_granularity的粒度(老版本默認(rèn)8192,新版本實(shí)現(xiàn)了自適應(yīng)粒度)被標(biāo)記成多個(gè)小的區(qū)間,其中每個(gè)區(qū)間最多8192行數(shù)據(jù),MergeTree使用MarkRange表示一個(gè)具體的區(qū)間,并通過(guò)start和end表示其具體的范圍。
如下圖所示。
MergeTree的存儲(chǔ)結(jié)構(gòu)index_granularity的命名雖然取了索引二字,但它不單只作用于一級(jí)索引(.idx),同時(shí)也會(huì)影響數(shù)據(jù)標(biāo)記(.mrk)和數(shù)據(jù)文件(.bin)。因?yàn)閮H有一級(jí)索引自身是無(wú)法完成查詢工作的,它需要借助數(shù)據(jù)標(biāo)記才能定位數(shù)據(jù),所以一級(jí)索引和數(shù)據(jù)標(biāo)記的間隔粒度相同(同為index_granularity行),彼此對(duì)齊。而數(shù)據(jù)文件也會(huì)依照index_granularity的間隔粒度生成壓縮數(shù)據(jù)塊。
索引的查詢過(guò)程
索引查詢其實(shí)就是兩個(gè)數(shù)值區(qū)間的交集判斷。其中,一個(gè)區(qū)間是由基于主鍵的查詢條件轉(zhuǎn)換而來(lái)的條件區(qū)間;而另一個(gè)區(qū)間是剛才所講述的與MarkRange對(duì)應(yīng)的數(shù)值區(qū)間。
整個(gè)索引的查詢過(guò)程可以分為三大步驟
生成查詢條件區(qū)間:將查詢條件轉(zhuǎn)換為條件區(qū)間。即便是單個(gè)值的查詢條件,也會(huì)被轉(zhuǎn)換成區(qū)間的形式。
- --舉例-- WHERE ID = 'A000' = ['A000', 'A000']WHERE ID > 'A000' = ('A000', '+inf')WHERE ID < 'A000' = ('-inf', 'A000')WHERE ID LIKE 'A000%' = ['A000', 'A001')
遞歸交集判斷:以遞歸的形式,依次對(duì)MarkRange的數(shù)值區(qū)間與條件區(qū)間做交集判斷。從最大的區(qū)間[A000 , +inf)開(kāi)始。
- 如果不存在交集,則直接通過(guò)剪枝算法優(yōu)化此整段MarkRange
- 如果存在交集,且MarkRange步長(zhǎng)大于N,則將這個(gè)區(qū)間進(jìn)一步拆分為N個(gè)子區(qū)間,并重復(fù)此規(guī)則,繼續(xù)做遞歸交集判斷(N由merge_tree_coarse_index_granularity指定,默認(rèn)值為8)
- 如果存在交集,且MarkRange不可再分解,則記錄MarkRange并返回
合并MarkRange區(qū)間:將最終匹配的MarkRange聚在一起,合并它們的范圍。
MergeTree通過(guò)遞歸的形式持續(xù)向下拆分區(qū)間,最終將MarkRange定位到最細(xì)的粒度,以幫助在后續(xù)讀取數(shù)據(jù)的時(shí)候,能夠最小化掃描數(shù)據(jù)的范圍。
聯(lián)合主鍵
當(dāng)我們以需要以多個(gè)字段為主鍵時(shí),此時(shí)數(shù)據(jù)的查詢和存儲(chǔ)就涉及到另外一種規(guī)則。
例如以 (CounterID, Date) 以主鍵,片段中數(shù)據(jù)首先按 CounterID 排序,具有相同 CounterID 的部分按 Date 排序。排序好的索引的圖示會(huì)是下面這樣:
全部數(shù)據(jù) : [-------------------------------------------------------------------------] CounterID: [aaaaaaaaaaaaaaaaaabbbbcdeeeeeeeeeeeeefgggggggghhhhhhhhhiiiiiiiiikllllllll] Date: [1111111222222233331233211111222222333211111112122222223111112223311122333] 標(biāo)記: | | | | | | | | | | |a,1 a,2 a,3 b,3 e,2 e,3 g,1 h,2 i,1 i,3 l,3 標(biāo)記號(hào): 0 1 2 3 4 5 6 7 8 9 10如果指定查詢?nèi)缦?#xff1a;
- CounterID in ('a', 'h'),服務(wù)器會(huì)讀取標(biāo)記號(hào)在 [0, 3) 和 [6, 8) 區(qū)間中的數(shù)據(jù)。
- CounterID IN ('a', 'h') AND Date = 3,服務(wù)器會(huì)讀取標(biāo)記號(hào)在 [1, 3) 和 [7, 8) 區(qū)間中的數(shù)據(jù)。
- Date = 3,服務(wù)器會(huì)讀取標(biāo)記號(hào)在 [1, 10] 區(qū)間中的數(shù)據(jù)。
上面例子可以看出使用索引通常會(huì)比全表描述要高效。
- 稀疏索引會(huì)引起額外的數(shù)據(jù)讀取。當(dāng)讀取主鍵單個(gè)區(qū)間范圍的數(shù)據(jù)時(shí),每個(gè)數(shù)據(jù)塊中最多會(huì)多讀 index_granularity * 2 行額外的數(shù)據(jù)。
- 稀疏索引使得你可以處理極大量的行,因?yàn)榇蠖鄶?shù)情況下,這些索引常駐與內(nèi)存(RAM)中。
從上面可以看出,ClickHouse的聯(lián)合主鍵在某種程度上與我們熟知的最左前綴規(guī)則有點(diǎn)類似,通常在以下幾種場(chǎng)景下我們才會(huì)考慮使用聯(lián)合索引
- 查詢會(huì)使用 b 列作為條件
- 很長(zhǎng)的數(shù)據(jù)范圍( index_granularity 的數(shù)倍)里 a 都是相同的值,并且這樣的情況很普遍。換言之,就是加入另一列后,可以讓你的查詢略過(guò)很長(zhǎng)的數(shù)據(jù)范圍。
- 數(shù)據(jù)量大,需要改善數(shù)據(jù)壓縮(以主鍵排序片段數(shù)據(jù),數(shù)據(jù)的一致性越高,壓縮越好)
長(zhǎng)的主鍵會(huì)對(duì)插入性能和內(nèi)存消耗有負(fù)面影響,但主鍵中額外的列并不影響 SELECT 查詢的性能。
二級(jí)索引
除了一級(jí)索引之外,MergeTree同樣支持二級(jí)索引。二級(jí)索引又稱跳數(shù)索引,由數(shù)據(jù)的聚合信息構(gòu)建而成。根據(jù)索引類型的不同,其聚合信息的內(nèi)容也不同。跳數(shù)索引的目的與一級(jí)索引一樣,也是幫助查詢時(shí)減少數(shù)據(jù)掃描的范圍。
(二級(jí)索引目前還處于測(cè)試階段,官方不建議大量使用)
跳數(shù)索引
目前,MergeTree共支持4種跳數(shù)索引,分別是minmax(最值)、set(集合行數(shù))、ngrambf_v1(N-Gram布隆過(guò)濾器)和tokenbf_v1(Token布隆過(guò)濾器)。一張數(shù)據(jù)表支持同時(shí)聲明多個(gè)跳數(shù)索引。
-
minmax(最值索引):minmax索引記錄了一段數(shù)據(jù)內(nèi)的最小和最大極值,其索引的作用類似分區(qū)目錄的minmax索引,能夠快速跳過(guò)無(wú)用的數(shù)據(jù)區(qū)間。
- 示例:INDEX [index_name] [column] TYPE minmax GRANULARITY [GRANULARITY SIZE]
-
set(集合行數(shù)索引):set索引直接記錄了聲明字段或表達(dá)式的不重復(fù)值,用于檢測(cè)數(shù)據(jù)塊是否滿足WHERE條件。
- 示例:INDEX [index_name] [column] TYPE set(max_rows) GRANULARITY [index_granularity]-- max_rows是一個(gè)閾值,表示在一個(gè)index_granularity內(nèi),索引最多記錄的數(shù)據(jù)行數(shù)。(如果max_rows=0,則表示無(wú)限制)
-
ngrambf_v1(N-Gram布隆過(guò)濾器):ngrambf_v1索引記錄的是指定長(zhǎng)度的數(shù)據(jù)短語(yǔ)的布隆表過(guò)濾器,只支持String和FixedString數(shù)據(jù)類型,同時(shí)只能夠提升in、notIn、like、equals和notEquals查詢的性能。
- 示例:INDEX [index_name] [column] TYPE ngrambf_v1(n, size_of_bloom_filter_in_bytes, number_of_hash_functions, random_seed) GRANULARITY [index_granularity]/* n:token長(zhǎng)度,依據(jù)n的長(zhǎng)度將數(shù)據(jù)切割為token短語(yǔ)。 size_of_bloom_filter_in_bytes:布隆過(guò)濾器的大小。 number_of_hash_functions:布隆過(guò)濾器中使用Hash函數(shù)的個(gè)數(shù)。 random_seed: Hash函數(shù)的隨機(jī)種子。 */
- 布隆過(guò)濾器可能會(huì)包含不符合條件的匹配,所以 ngrambf_v1, tokenbf_v1 和 bloom_filter 索引不能用于負(fù)向的函數(shù),例如:--可以用來(lái)優(yōu)化的場(chǎng)景 s LIKE '%test%' NOT s NOT LIKE '%test%' s = 1 NOT s != 1 startsWith(s, 'test') i --不能用來(lái)優(yōu)化的場(chǎng)景 NOT s LIKE '%test%' s NOT LIKE '%test%' NOT s = 1 s != 1 NOT startsWith(s, 'test')
-
tokenbf_v1(Token布隆過(guò)濾器):tokenbf_v1索引是ngrambf_v1的變種,同樣也是一種布隆過(guò)濾器索引。tokenbf_v1除了短語(yǔ)token的處理方法外,其他與ngrambf_v1是完全一樣的。tokenbf_v1會(huì)自動(dòng)按照非字符的、數(shù)字的字符串分割token。
- 示例:INDEX d ID TYPE tokenbf_v1(size_of_bloom_filter_in_bytes, number_of_hash_functions, random_seed)
granularity
對(duì)于跳數(shù)索引而言,index_granularity定義了數(shù)據(jù)的粒度,而granularity定義了聚合信息匯總的粒度。換言之,granularity定義了一行跳數(shù)索引能夠跳過(guò)多少個(gè)index_granularity區(qū)間的數(shù)據(jù)。
作用規(guī)則如下:首先,按照index_granularity粒度間隔將數(shù)據(jù)劃分成n段,總共有[0 , n-1]個(gè)區(qū)間(n = total_rows /index_granularity,向上取整)。接著,根據(jù)索引定義時(shí)聲明的表達(dá)式,從0區(qū)間開(kāi)始,依次按index_granularity粒度從數(shù)據(jù)中獲取聚合信息,每次向前移動(dòng)1步(n+1),聚合信息逐步累加。最后,當(dāng)移動(dòng)granularity次區(qū)間時(shí),則匯總并生成一行跳數(shù)索引數(shù)據(jù)。
以minmax索引為例,假設(shè)index_granularity=8192且granularity=3,則數(shù)據(jù)會(huì)按照index_granularity劃分為n等份,MergeTree從第0段分區(qū)開(kāi)始,依次獲取聚合信息。當(dāng)獲取到第3個(gè)分區(qū)時(shí)(granularity=3),則匯總并會(huì)生成第一行minmax索引(前3段minmax極值匯總后取值為[1 , 9]),如下圖
granularity作用規(guī)則數(shù)據(jù)標(biāo)記
如果把MergeTree比作一本書(shū),primary.idx一級(jí)索引好比這本書(shū)的一級(jí)章節(jié)目錄,.bin文件中的數(shù)據(jù)好比這本書(shū)中的文字,那么數(shù)據(jù)標(biāo)記(.mrk)就好比書(shū)簽一樣,會(huì)為一級(jí)章節(jié)目錄和具體的文字之間建立關(guān)聯(lián)。
對(duì)于數(shù)據(jù)標(biāo)記而言,它記錄了兩點(diǎn)重要信息:
- 一級(jí)章節(jié)對(duì)應(yīng)的頁(yè)碼信息。
- 一段文字在某一頁(yè)中的起始位置信息。
這樣一來(lái),通過(guò)數(shù)據(jù)標(biāo)記就能夠很快地從一本書(shū)中立即翻到關(guān)注內(nèi)容所在的那一頁(yè),并知道從第幾行開(kāi)始閱讀。
標(biāo)記數(shù)據(jù)與一級(jí)索引數(shù)據(jù)不同,它并不能常駐內(nèi)存,而是使用LRU(最近最少使用)緩存策略加快其取用速度。
生成規(guī)則
通過(guò)索引下標(biāo)編號(hào)找到對(duì)應(yīng)的數(shù)據(jù)標(biāo)記從上圖可以看出,數(shù)據(jù)標(biāo)記和索引區(qū)間是對(duì)齊的,均按照index_granularity的粒度間隔。如此一來(lái),只需簡(jiǎn)單通過(guò)索引區(qū)間的下標(biāo)編號(hào)就可以直接找到對(duì)應(yīng)的數(shù)據(jù)標(biāo)記。
為了能夠與數(shù)據(jù)銜接,數(shù)據(jù)標(biāo)記文件也與.bin文件一一對(duì)應(yīng)。即每一個(gè)列字段[Column].bin文件都有一個(gè)與之對(duì)應(yīng)的[Column].mrk數(shù)據(jù)標(biāo)記文件,用于記錄數(shù)據(jù)在.bin文件中的偏移量信息。同時(shí),.mrk包含了.bin壓縮和解壓縮這兩種不同狀態(tài)的偏移量,如下圖
標(biāo)記數(shù)據(jù)示意圖工作方式
MergeTree在讀取數(shù)據(jù)時(shí),必須通過(guò)標(biāo)記數(shù)據(jù)的位置信息才能夠找到所需要的數(shù)據(jù)。整個(gè)查找過(guò)程大致可以分為讀取壓縮數(shù)據(jù)塊和讀取數(shù)據(jù)兩個(gè)步驟。
對(duì)于下圖來(lái)說(shuō),表的index_granularity粒度為8192,所以一個(gè)索引片段的數(shù)據(jù)大小恰好是8192B。按照壓縮數(shù)據(jù)塊的生成規(guī)則,如果單個(gè)批次數(shù)據(jù)小于64KB,則繼續(xù)獲取下一批數(shù)據(jù),直至累積到size>=64KB時(shí),生成下一個(gè)壓縮數(shù)據(jù)塊。因此在JavaEnable的標(biāo)記文件中,每8行標(biāo)記數(shù)據(jù)對(duì)應(yīng)1個(gè)壓縮數(shù)據(jù)塊(1B * 8192 = 8192B, 64KB = 65536B, 65536 / 8192 =8)。
從圖能夠看到,其左側(cè)的標(biāo)記數(shù)據(jù)中,8行數(shù)據(jù)的壓縮文件偏移量都是相同的,因?yàn)檫@8行標(biāo)記都指向了同一個(gè)壓縮數(shù)據(jù)塊。而在這8行的標(biāo)記數(shù)據(jù)中,它們的解壓縮數(shù)據(jù)塊中的偏移量,則依次按照8192B(每行數(shù)據(jù)1B,每一個(gè)批次8192行數(shù)據(jù))累加,當(dāng)累加達(dá)到65536(64KB)時(shí)則置0。因?yàn)楦鶕?jù)規(guī)則,此時(shí)會(huì)生成下一個(gè)壓縮數(shù)據(jù)塊。
JavaEnable字段的標(biāo)記文件和壓縮數(shù)據(jù)文件的對(duì)應(yīng)關(guān)系數(shù)據(jù)標(biāo)記與壓縮數(shù)據(jù)塊的對(duì)應(yīng)關(guān)系
由于壓縮數(shù)據(jù)塊的劃分,與一個(gè)間隔index_granularity內(nèi)的數(shù)據(jù)大小相關(guān),每個(gè)壓縮數(shù)據(jù)塊的體積都被嚴(yán)格控制在64KB~1MB。而一個(gè)間隔index_granularity的數(shù)據(jù),又只會(huì)產(chǎn)生一行數(shù)據(jù)標(biāo)記。那么根據(jù)一個(gè)間隔內(nèi)數(shù)據(jù)的實(shí)際字節(jié)大小,數(shù)據(jù)標(biāo)記和壓縮數(shù)據(jù)塊之間會(huì)產(chǎn)生三種不同的對(duì)應(yīng)關(guān)系。
- 一對(duì)一
- 一個(gè)數(shù)據(jù)標(biāo)記對(duì)應(yīng)一個(gè)壓縮數(shù)據(jù)塊,當(dāng)一個(gè)間隔index_granularity內(nèi)的數(shù)據(jù)未壓縮大小size大于等于64KB且小于等于1MB時(shí),會(huì)出現(xiàn)這種對(duì)應(yīng)關(guān)系。
- 一個(gè)數(shù)據(jù)標(biāo)記對(duì)應(yīng)一個(gè)壓縮數(shù)據(jù)塊,當(dāng)一個(gè)間隔index_granularity內(nèi)的數(shù)據(jù)未壓縮大小size大于等于64KB且小于等于1MB時(shí),會(huì)出現(xiàn)這種對(duì)應(yīng)關(guān)系。
- 一對(duì)多
- 一個(gè)數(shù)據(jù)標(biāo)記對(duì)應(yīng)多個(gè)壓縮數(shù)據(jù)塊,當(dāng)一個(gè)間隔index_granularity內(nèi)的數(shù)據(jù)未壓縮大小size直接大于1MB時(shí),會(huì)出現(xiàn)這種對(duì)應(yīng)關(guān)系。
- 一個(gè)數(shù)據(jù)標(biāo)記對(duì)應(yīng)多個(gè)壓縮數(shù)據(jù)塊,當(dāng)一個(gè)間隔index_granularity內(nèi)的數(shù)據(jù)未壓縮大小size直接大于1MB時(shí),會(huì)出現(xiàn)這種對(duì)應(yīng)關(guān)系。
- 多對(duì)一
- 多個(gè)數(shù)據(jù)標(biāo)記對(duì)應(yīng)一個(gè)壓縮數(shù)據(jù)塊,當(dāng)一個(gè)間隔index_granularity內(nèi)的數(shù)據(jù)未壓縮大小size小于64KB時(shí),會(huì)出現(xiàn)這種對(duì)應(yīng)關(guān)系。
- 多個(gè)數(shù)據(jù)標(biāo)記對(duì)應(yīng)一個(gè)壓縮數(shù)據(jù)塊,當(dāng)一個(gè)間隔index_granularity內(nèi)的數(shù)據(jù)未壓縮大小size小于64KB時(shí),會(huì)出現(xiàn)這種對(duì)應(yīng)關(guān)系。
工作流程
存儲(chǔ)流程
數(shù)據(jù)的存儲(chǔ)流程主要有以下幾個(gè)步驟
- 首先生成分區(qū)目錄,伴隨著每一批數(shù)據(jù)的寫入,都會(huì)生成一個(gè)新的分區(qū)目錄。
- 在后續(xù)的某一時(shí)刻,屬于相同分區(qū)的目錄會(huì)依照規(guī)則合并到一起
- 接著,按照index_granularity索引粒度,會(huì)分別生成primary.idx一級(jí)索引(如果聲明了二級(jí)索引,還會(huì)創(chuàng)建二級(jí)索引文件)、每一個(gè)列字段的.mrk數(shù)據(jù)標(biāo)記和.bin壓縮數(shù)據(jù)文件。
查詢流程
數(shù)據(jù)查詢的本質(zhì),可以看作一個(gè)不斷減小數(shù)據(jù)范圍的過(guò)程。在最理想的情況下,MergeTree首先可以依次借助分區(qū)索引、一級(jí)索引和二級(jí)索引,將數(shù)據(jù)掃描范圍縮至最小。然后再借助數(shù)據(jù)標(biāo)記,將需要解壓與計(jì)算的數(shù)據(jù)范圍縮至最小。
將掃描數(shù)據(jù)范圍最小化的過(guò)程如果一條查詢語(yǔ)句沒(méi)有指定任何WHERE條件,或是指定了WHERE條件,但條件沒(méi)有匹配到任何索引(分區(qū)索引、一級(jí)索引和二級(jí)索引),那么MergeTree就不能預(yù)先減小數(shù)據(jù)范圍。在后續(xù)進(jìn)行數(shù)據(jù)查詢時(shí),它會(huì)掃描所有分區(qū)目錄,以及目錄內(nèi)索引段的最大區(qū)間。雖然不能減少數(shù)據(jù)范圍,但是MergeTree仍然能夠借助數(shù)據(jù)標(biāo)記,以多線程的形式同時(shí)讀取多個(gè)壓縮數(shù)據(jù)塊,以提升性能。
總結(jié)
以上是生活随笔為你收集整理的ClickHouse 数据存储原理:MergeTree引擎的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 还搞不懂STL的type_traits?
- 下一篇: ClickHouse 副本协同原理:Re