ClickHouse和他的朋友們(10)MergeTree Write-Ahead Log

原文出處:https://bohutang.me/2020/08/18/clickhouse-and-friends-merge-tree-wal/


最後更新: 2020-09-18

數據庫系統爲了提高寫入性能,會把數據先寫到內存,等“攢”到一定程度後再回寫到磁盤,比如 MySQL 的 buffer pool 機制。

因爲數據先寫到內存,爲了數據的安全性,我們需要一個 Write-Ahead Log (WAL) 來保證內存數據的安全性。

今天我們來看看 ClickHouse 新增的 MergeTreeWriteAheadLog (https://github.com/ClickHouse/ClickHouse/pull/8290) 模塊,它到底解決了什麼問題。

高頻寫問題

對於 ClickHouse MergeTree 引擎,每次寫入(即使1條數據)都會在磁盤生成一個分區目錄(part),等着 merge 線程合併。

如果有多個客戶端,每個客戶端寫入的數據量較少、次數較頻繁的情況下,就會引發 DB::Exception: Too many parts 錯誤。

這樣就對客戶端有一定的要求,比如需要做 batch 寫入。

或者,寫入到 Buffer 引擎,定時的刷回 MergeTree,缺點是在宕機時可能會丟失數據。

MergeTree WAL

1. 默認模式

我們先看看在沒有 WAL 情況下,MergeTree 是如何寫入的:

每次寫入 MergeTree 都會直接在磁盤上創建分區目錄,並生成分區數據,這種模式其實就是 WAL + 數據的融合。

很顯然,這種模式不適合頻繁寫操作的情況,否則會生成非常多的分區目錄和文件,引發 Too many parts 錯誤。

2. WAL模式

設置SETTINGS: min_rows_for_compact_part=2,分別執行2條寫 SQL,數據會先寫到 wal.bin 文件:

當滿足 min_rows_for_compact_part=2 後,merger 線程觸發合併操作,生成 1_1_2_1 分區,也就是完成了 wal.bin 裏的 1_1_1_0 和 1_2_2_0 兩個分區的合併操作。當我們執行第三條 SQL 寫入:

insert into default.mt(a,b,c) values(1,3,3)

數據塊(分區)會繼續追加到 wal.bin 尾部:

此時,3 條數據分佈在兩個地方:分區 1_1_2_1, wal.bin 裏的 1_3_3_0

這樣就有一個問題:當我們執行查詢的時候,數據是怎麼合併的呢?

MergeTree 使用全局結構 data_parts_indexes 維護分區信息,當服務啓動的時候,MergeTreeData::loadDataParts方法:

1. data_parts_indexes.insert(1_1_2_1)
2. 讀取 wal.bin,通過 getActiveContainingPart 判斷分區是否已經merge到磁盤:1_1_1_0 已經存在, 1_2_2_0 已經存在,data_parts_indexes.insert(1_3_3_0)
3. data_parts_indexes:{1_1_2_1,1_3_3_0} 

這樣,它總是能維護全局的分區信息。

總結

WAL 功能在 PR#8290 (https://github.com/ClickHouse/ClickHouse/pull/8290) 實現,master 分支已經默認開啓。

MergeTree 通過 WAL 來保護客戶端的高頻、少量寫機制,減少服務端目錄和文件數量,讓客戶端操作儘可能簡單、高效。


全文完。


Enjoy ClickHouse :)


葉老師的「MySQL核心優化」大課已升級到MySQL 8.0,掃碼開啓MySQL 8.0修行之旅吧



本文分享自微信公衆號 - 老葉茶館(iMySQL_WX)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章