MergeTree原理詳解之資料儲存

2021-10-12 10:12:33 字數 3553 閱讀 7563

前面我們講解了mergetree引擎索引的原理,但是僅僅依靠索引,並不能支撐clickhouse如此強悍的效能。這篇文章將為你解決以下問題,資料在底層具體是如何儲存,怎麼根據索引編號找到對應的資料。

說到儲存,大部分mpp資料庫都是用的同一種思想,即列式儲存。clickhouse也不例外。

在mergetree中,資料按照列儲存,注意哦,是完全列式儲存,每個列字段都擁有乙個與之對應的.bin檔案,這些檔案承載著資料的物理儲存。資料檔案以分割槽目錄的形式被組織儲存,在bin檔案中只會儲存當前分割槽片段內的這部分資料。

這樣存有以下優點:

可以更好的進行資料壓縮,相同型別的資料放在一起,對壓縮更加友好

能夠最小化資料掃瞄的範圍。

mergetree將資料寫入.bin檔案之前需要經過以下處理:

3. 資料需要經過壓縮,預設是lz4演算法

4. 資料會事先按照order by的宣告排序

5. 資料是以壓縮資料塊的形式被組織並寫入.bin檔案的

.bin檔案中有多個壓縮資料塊,每個壓縮資料塊的體積,按照其壓縮前的資料位元組大小,都被嚴格控制在64kb到1mb之間,分別由min_compress_block_size(預設64k)和max_compress_block_size(預設1m)指定。
組成部分:

乙個壓縮資料塊由頭部資訊和壓縮資料兩部分組成。頭部資訊由壓縮演算法型別、壓縮後的資料大小和壓縮前的資料大小組成,分別為1個uint8(1位元組)整型和2個uint32(4位元組)整型組成。

壓縮資料塊的生成

mergetree在資料寫入的過程中,會按照索引粒度(每隔8192行),按批次獲取資料進行處理。過程如下:

單個批次資料64kb單個批次資料size>1mb:首先按照1mb大小截斷並生成下乙個壓縮資料塊。剩餘資料繼續依照上述規則執行。此時會出現乙個批次資料生成多個壓縮資料塊的情況。

結論:乙個bin檔案是由乙個或多個壓縮資料塊組成,每個壓縮塊的大小在64kb~1mb之間,多個資料塊之間按照寫入順序首尾相接,緊密地排列在一起。且每個資料間隔與壓縮資料塊存在三種關係:一對多,一對一,多對一。

這麼做有什麼好處呢?

其實這是clickhouse在效能損耗和壓縮率之間的平衡,壓縮後的資料能夠有效減少資料大小,降低儲存空間並加速資料傳輸效率,但是資料的壓縮和解壓縮本身也會帶來額外的效能損耗,所以必須控制壓縮資料塊的大小。

其次在具體讀取某一列資料的時候,首先要將資料載入到記憶體並解壓,這樣才能進行後續的資料處理,通過壓縮資料塊的方式,clickhouse並不用把整個bin檔案讀取到記憶體中,只需讀取bin檔案中特定的壓縮塊即可,從而進一步縮小了資料讀取的範圍。

那麼問題來了,clickhouse是怎麼根據索引找到指定的壓縮塊的呢?那就要用到我們接下來要講的資料標記了。

資料標記用於連線一級索引和資料。資料標記與索引區間是對齊的,都按照index_granularity的粒度間隔,因此只要找到對應的索引區間,就能找到資料標記

為了能夠與資料銜接,資料標記檔案與bin檔案一一對應。即每個列欄位[column].bin都有乙個與之對應的[column].mrk資料標記檔案,用於記錄資料在bin檔案的偏移量資訊。

值得注意的是:標記資料和一級索引資料不同,它並不常駐記憶體,而是使用lru(最近最少使用)儲存策略加快其讀取速度。

資料標記的生成

資料標記是用來連線索引和資料的,由前面我們知道,資料標記和索引區間是對齊的,均按照index_granularity的粒度間隔。這樣的話,我們只要找到索引編號就能直接找到對應的資料標記。

在資料標記檔案中(.mrk),一行標記使用乙個元祖表示,(此資料區間在.bin壓縮資料塊的起始位置,未壓縮資料的起始偏移位置)

如下圖所示:

(1)在對應的.bin壓縮檔案中,壓縮資料塊的起始偏移量

(2)在對應的.bin壓縮檔案中,將該資料壓縮塊解壓後的起始偏移量

上圖中每一行資料標記與資料間隔(index_granularity)對應,假設兩個資料間隔即16384行的大小恰好超過了64kb,此時根據資料塊生成原則,生成了第乙個壓縮資料塊,該壓縮塊的偏移量為[0,12016]。

那麼這裡有個問題,這裡的12016就代表著資料塊的大小嗎?

其實並不是,前面我們說到壓縮資料塊包含兩個部分,一部分是標頭檔案,另一部分才是資料壓縮。那麼這12016就可以簡單的用一下圖表示:

壓縮資料塊讀取資料流程

上面我們了解了壓縮資料塊的基本構造以及生成規則,下面我們再給大家講講壓縮資料塊讀取資料的流程。

查詢過程可以分為讀取壓縮資料塊和讀取資料兩個步驟。

(1)讀取壓縮資料塊:在查詢某一列資料時,mergetree無須一次性載入整個.bin檔案,而是可以根據需要,只載入特定的壓縮資料塊。那麼就得通過我們上面所講的資料標記檔案來實現。兩個相鄰的壓縮資料塊的起始偏移量構成了當前標記對應的壓縮資料塊的偏移量區間。例如上圖,在讀取時,就可以直接讀取.bin檔案的[0,12016]位元組的資料,就可以獲得第0個壓縮資料塊。

(2)讀取資料:讀取壓縮資料塊後,mergetree也並不需要掃瞄整個解壓資料。前面我們講到資料標記檔案的構造中就提到,每個一行代表乙個索引區間,不但對應著壓縮資料塊的起始位置,還對應著解壓後的資料塊的起始偏移量,還是以上圖為例,假如我們要查詢的資料對應著索引區間1,那麼此時我拿到壓縮資料塊0解壓之後,只需從解壓資料塊的57344位元組位置開始掃瞄。

寫入過程

查詢過程

資料的查詢就是乙個不斷減小範圍的過程。理論上,最理想的情況下,mergetree首先可以依次借助分割槽索引、一級索引和二級索引,將資料掃瞄範圍縮至最小。然後借助資料標記,定位到需要進行計算的資料範圍。

最不理想的情況下是,一條查詢語句沒有指定任何where條件,或者where條件沒有使用到任何索引(分割槽索引、一級索引和二級索引),那麼它會掃瞄所有分割槽目錄,一級目錄內索引段的最大區間。雖然沒辦法減少資料掃瞄的範圍,但是mergetree可以借助資料標記,以多執行緒的形式同時讀取多個壓縮資料塊,以提公升效能。

結合上一章索引的原理,我們可以說對mergetree進行了乙個全方位的剖析,首先是mergetree的基礎屬性和物理儲存結構,還有資料分割槽、一級索引、二級索引、資料儲存和資料標記的重要概念。然後介紹了它們是如何進行協同作用的。相信到這你已經對mergetree引擎有乙個比較全的了解了,在後面的文章中,我們將進行實操,動手用起來。

Java併發之AQS原理詳解

一 abstractqueuedsynchronizer 的用途 下面簡稱 aqs,jdk 1.8 aqs是借助 fifo等待佇列,用來實現同步器的同步框架,通俗的來說,它是用來實現鎖的工具,一般來說,它需要實現這些功能 二 aqs 的工作原理 aqs的核心是基於鍊錶實現的 fifo等待佇列,該佇列...

晶振詳解之工作原理

晶體or晶振,還是傻傻分不清楚?晶體是晶體諧振器 crystal 的簡稱,也稱為無源晶體。一般是利用石英晶體的壓電效應,用來產生高精度振盪頻率的一種電子元件。無源晶體需要依靠電容電阻才能進行起振。晶振是晶體振盪器 oscillator 的簡稱,也稱為有源晶振。有源晶振內部整合了起振電路,無需外部新增...

以太坊工作原理之txpool詳解

交易池txpool作為區塊鏈系統的重要組成部分,對系統的安全性和穩定性具有重要作用。功能可歸納為 交易快取 交易驗證和交易過濾。txpool主要包含兩個重要的緩衝區 pending和queue。交易在進行打包驗證和p2p廣播前,首先要通過txpool來進行層層驗證,驗證過的交易會被換存在pendin...