ClickHouse 原理 | ClickHouse 存算分離架構(gòu)探索

ClickHouse 作為開源 OLAP 引擎,因其出色的性能表現(xiàn)在大數(shù)據(jù)生態(tài)中得到了廣泛的應(yīng)用。區(qū)別于 Hadoop 生態(tài)組件通常依賴 HDFS 作為底層的數(shù)據(jù)存儲,ClickHouse 使用本地盤來自己管理數(shù)據(jù),官方推薦使用 SSD 作為存儲介質(zhì)來提升性能。但受限于本地盤的容量上限以及 SSD 盤的價格,用戶很難在容量、成本和性能這三者之間找到一個好的平衡。JuiceFS 的某個客戶近期就遇到了這樣的難題,希望將 ClickHouse 中的溫冷數(shù)據(jù)從 SSD 盤遷移到更大容量、更低成本的存儲介質(zhì),更好地支撐業(yè)務(wù)查詢更長時間數(shù)據(jù)的需求。
JuiceFS 是基于對象存儲實現(xiàn)并完全兼容 POSIX 的開源分布式文件系統(tǒng),同時 JuiceFS 的數(shù)據(jù)緩存特性可以智能管理查詢熱點數(shù)據(jù),非常適合作為 ClickHouse 的存儲系統(tǒng),下面將詳細(xì)介紹這個方案。
在介紹具體方案之前先簡單了解一下 MergeTree 的存儲格式。MergeTree 是 ClickHouse 最主要使用的存儲引擎,當(dāng)創(chuàng)建表時可以通過 PARTITION BY 語句指定以某一個或多個字段作為分區(qū)字段,數(shù)據(jù)在磁盤上的目錄結(jié)構(gòu)類似如下形式:
$ ls -l /var/lib/clickhouse/data/<database>/<table>
drwxr-xr-x 2 test test 64B Mar 8 13:46 202102_1_3_0
drwxr-xr-x 2 test test 64B Mar 8 13:46 202102_4_6_1
drwxr-xr-x 2 test test 64B Mar 8 13:46 202103_1_1_0
drwxr-xr-x 2 test test 64B Mar 8 13:46 202103_4_4_0以 202102_1_3_0 為例,202102 是分區(qū)的名稱,1 是最小的數(shù)據(jù)塊編號,3 是最大的數(shù)據(jù)塊編號,0 是 MergeTree 的深度。可以看到 202102 這個分區(qū)不止一個目錄,這是因為 ClickHouse 每次在寫入的時候都會生成一個新的目錄,并且一旦寫入以后就不會修改(immutable)。每一個目錄稱作一個「part」,當(dāng) part 逐漸變多以后 ClickHouse 會在后臺對多個 part 進(jìn)行合并(compaction),通常的建議是不要保留過多 part,否則會影響查詢性能。
每個 part 目錄內(nèi)部又由很多大大小小的文件組成,這里面既有數(shù)據(jù),也有一些元信息,一個典型的目錄結(jié)構(gòu)如下所示:
$ ls -l /var/lib/clickhouse/data/<database>/<table>/202102_1_3_0
-rw-r--r-- 1 test test ?? Mar 8 14:06 ColumnA.bin
-rw-r--r-- 1 test test ?? Mar 8 14:06 ColumnA.mrk
-rw-r--r-- 1 test test ?? Mar 8 14:06 ColumnB.bin
-rw-r--r-- 1 test test ?? Mar 8 14:06 ColumnB.mrk
-rw-r--r-- 1 test test ?? Mar 8 14:06 checksums.txt
-rw-r--r-- 1 test test ?? Mar 8 14:06 columns.txt
-rw-r--r-- 1 test test ?? Mar 8 14:06 count.txt
-rw-r--r-- 1 test test ?? Mar 8 14:06 minmax_ColumnC.idx
-rw-r--r-- 1 test test ?? Mar 8 14:06 partition.dat
-rw-r--r-- 1 test test ?? Mar 8 14:06 primary.idx
其中比較重要的文件有:
primary.idx:這個文件包含的是主鍵信息,但不是當(dāng)前 part 全部行的主鍵,默認(rèn)會按照 8192 這個區(qū)間來存儲,也就是每 8192 行存儲一次主鍵。ColumnA.bin:這是壓縮以后的某一列的數(shù)據(jù),ColumnA只是這一列的代稱,實際情況會是真實的列名。壓縮是以 block 作為最小單位,每個 block 的大小從 64KiB 到 1MiB 不等。ColumnA.mrk:這個文件保存的是對應(yīng)的ColumnA.bin文件中每個 block 壓縮后和壓縮前的偏移。partition.dat:這個文件包含的是經(jīng)過分區(qū)表達(dá)式計算以后的分區(qū) ID。minmax_ColumnC.idx:這個文件包含的是分區(qū)字段對應(yīng)的原始數(shù)據(jù)的最小值和最大值。
因為 JuiceFS 完全兼容 POSIX,所以可以把 JuiceFS 掛載的文件系統(tǒng)直接作為 ClickHouse 的磁盤來使用。這種方案下數(shù)據(jù)會直接寫入 JuiceFS,結(jié)合為 ClickHouse 節(jié)點配置的緩存盤,查詢時涉及的熱數(shù)據(jù)會自動緩存在 ClickHouse 節(jié)點本地。整體方案如下圖所示。

ClickHouse 在寫入時會產(chǎn)生大量的小文件,因此如果寫入壓力較大這個方案對寫入和查詢性能都會有一定影響。建議在寫入數(shù)據(jù)時增大寫入緩存,盡量一次寫入更多數(shù)據(jù)來避免這個小文件過多的問題。最簡單的做法是使用 ClickHouse 的 Buffer 表,基本上不需要修改應(yīng)用代碼就可以解決小文件過多的問題,適合當(dāng) ClickHouse 宕機(jī)時允許少量數(shù)據(jù)丟失的場景。這樣做的好處是存儲和計算完全分離,ClickHouse 節(jié)點完全無狀態(tài),如果節(jié)點故障可以很快恢復(fù),不涉及任何數(shù)據(jù)拷貝。未來可以讓 ClickHouse 感知到底層存儲是共享的,實現(xiàn)自動的無數(shù)據(jù)拷貝遷移。
同時由于 ClickHouse 通常應(yīng)用在實時分析場景,這個場景對于數(shù)據(jù)實時更新的要求比較高,在分析時也需要經(jīng)常性地查詢新數(shù)據(jù)。因此數(shù)據(jù)具有比較明顯的冷熱特征,即一般新數(shù)據(jù)是熱數(shù)據(jù),隨著時間推移歷史數(shù)據(jù)逐漸變?yōu)槔鋽?shù)據(jù)。利用 ClickHouse 的存儲策略(storage policy)來配置多塊磁盤,通過一定條件可以實現(xiàn)自動遷移冷數(shù)據(jù)到 JuiceFS。整體方案如下圖所示。

這個方案中數(shù)據(jù)會先寫入本地磁盤,當(dāng)滿足一定條件時 ClickHouse 的后臺線程會異步把數(shù)據(jù)從本地磁盤遷移到 JuiceFS 上。和第一個方案一樣,查詢時也會自動緩存熱數(shù)據(jù)。注意圖中為了區(qū)分寫和讀因此畫了兩塊磁盤,實際使用中沒有這個限制,可以使用同一個盤。雖然這個方案不是完全的存儲計算分離,但是可以滿足對寫入性能要求特別高的場景需求,也保留一定的存儲資源彈性伸縮能力。下面會詳細(xì)介紹這個方案在 ClickHouse 中如何配置。
ClickHouse 支持配置多塊磁盤用于數(shù)據(jù)存儲,下面是示例的配置文件:
<storage_configuration>
<disks>
<jfs>
<path>/jfs</path>
</jfs>
</disks>
</storage_configuration>
上面的 /jfs 目錄即是 JuiceFS 文件系統(tǒng)掛載的路徑。在把以上配置添加到 ClickHouse 的配置文件中,并成功掛載 JuiceFS 文件系統(tǒng)以后,就可以通過 MOVE PARTITION 命令將某個 partition 移動到 JuiceFS 上,例如:
ALTER TABLE test MOVE PARTITION 'xxx' TO DISK 'jfs';
當(dāng)然這種手動移動的方式只是用于測試,ClickHouse 支持通過配置存儲策略的方式來將數(shù)據(jù)自動從某個磁盤移動到另一個磁盤。下面是示例的配置文件:
<storage_configuration>
<disks>
<jfs>
<path>/jfs</path>
</jfs>
</disks>
<policies>
<hot_and_cold>
<volumes>
<hot>
<disk>default</disk>
<max_data_part_size_bytes>1073741824</max_data_part_size_bytes>
</hot>
<cold>
<disk>jfs</disk>
</cold>
</volumes>
<move_factor>0.1</move_factor>
</hot_and_cold>
</policies>
</storage_configuration>
上面的配置文件中有一個名為 hot_and_cold 的存儲策略,其中定義了兩個 volume,名為 hot 的 volume 是默認(rèn)的 SSD 盤,名為 cold 的 volume 即是上一步 disks 中定義的 JuiceFS 盤。這些 volume 在配置文件中的順序很重要,數(shù)據(jù)會首先存儲到第一個 volume 中,而 max_data_part_size_bytes 這個配置表示當(dāng)數(shù)據(jù) part 超過指定的大小時(示例中是 1GiB)自動從當(dāng)前 volume 移動到下一個 volume,也就是把數(shù)據(jù)從 SSD 盤移動到 JuiceFS。最后的 move_factor 配置表示當(dāng) SSD 盤的磁盤容量超過 90% 時也會觸發(fā)數(shù)據(jù)移動到 JuiceFS。
最后在創(chuàng)建表時需要顯式指定要用到的存儲策略:
CREATE TABLE test (
...
) ENGINE = MergeTree
...
SETTINGS storage_policy = 'hot_and_cold';
當(dāng)滿足數(shù)據(jù)移動的條件時,ClickHouse 就會啟動后臺線程去執(zhí)行移動數(shù)據(jù)的操作,默認(rèn)會有 8 個線程同時工作,這個線程數(shù)量可以通過 background_move_pool_size 配置調(diào)整。
除了配置存儲策略以外,還可以在創(chuàng)建表時通過 TTL 將超過一段時間的數(shù)據(jù)移動到 JuiceFS 上,例如:
CREATE TABLE test (
d DateTime,
...
) ENGINE = MergeTree
...
TTL d + INTERVAL 1 DAY TO DISK 'jfs'
SETTINGS storage_policy = 'hot_and_cold';
上面的例子是將超過 1 天的數(shù)據(jù)移動到 JuiceFS 上,結(jié)合存儲策略一起可以非常靈活地管理數(shù)據(jù)的生命周期。
采用冷熱數(shù)據(jù)分離方案以后數(shù)據(jù)并不會直接寫入 JuiceFS,而是先寫入 SSD 盤,再通過后臺線程異步遷移到 JuiceFS 上。但是我們希望直接評估不同存儲介質(zhì)在寫數(shù)據(jù)的場景有多大的性能差異,因此這里在測試寫入性能時沒有配置冷熱數(shù)據(jù)分離的存儲策略,而是讓 ClickHouse 直接寫入不同的存儲介質(zhì)。
具體測試方法是將真實業(yè)務(wù)中的某一張 ClickHouse 表作為數(shù)據(jù)源,然后使用 INSERT INTO 語句批量插入千萬級行數(shù)的數(shù)據(jù),比較直接寫入 SSD 盤、JuiceFS 以及對象存儲的吞吐。最終的測試結(jié)果如下圖:

以 SSD 盤作為基準(zhǔn),可以看到 JuiceFS 的寫入性能與 SSD 盤有 30% 左右的性能差距,但是相比對象存儲有 11 倍的性能提升。這里 JuiceFS 的測試中開啟了 writeback 選項,這是因為 ClickHouse 在寫入時每個 part 會產(chǎn)生大量的小文件(KiB 級),客戶端采用異步寫入的方式能明顯提升性能,同時大量的小文件對于查詢性能也會造成一定影響。
在了解了直接寫入不同介質(zhì)的性能以后,接下來測試?yán)錈釘?shù)據(jù)分離方案的寫入性能。經(jīng)過實際業(yè)務(wù)測試,基于 JuiceFS 的冷熱數(shù)據(jù)分離方案表現(xiàn)穩(wěn)定,因為新數(shù)據(jù)都是直接寫入 SSD 盤,因此寫入性能與上面測試中的 SSD 盤性能相當(dāng)。SSD 盤上的數(shù)據(jù)可以很快遷移到 JuiceFS 上,在 JuiceFS 上對數(shù)據(jù) part 進(jìn)行合并也都是沒有問題的。
查詢性能測試使用真實業(yè)務(wù)中的數(shù)據(jù),并選取幾個典型的查詢場景進(jìn)行測試。其中 q1-q4 是掃描全表的查詢,q5-q7 是命中主鍵索引的查詢。測試結(jié)果如下圖:

可以看到 JuiceFS 與 SSD 盤的查詢性能基本相當(dāng),平均差異在 6% 左右,但是對象存儲相比 SSD 盤有 1.4 至 30 倍的性能下降。得益于 JuiceFS 高性能的元數(shù)據(jù)操作以及本地緩存特性,可以自動將查詢請求需要的熱數(shù)據(jù)緩存在 ClickHouse 節(jié)點本地,大幅提升了 ClickHouse 的查詢性能。需要注意的是以上測試中對象存儲是通過 ClickHouse 的 S3 磁盤類型進(jìn)行訪問,這種方式只有數(shù)據(jù)是存儲在對象存儲上,元數(shù)據(jù)還是在本地磁盤。如果通過類似 S3FS 的方式把對象存儲掛載到本地,性能會有進(jìn)一步的下降。
在完成基礎(chǔ)的查詢性能測試以后,接下來測試?yán)錈釘?shù)據(jù)分離方案下的查詢性能。區(qū)別于前面的測試,當(dāng)采用冷熱數(shù)據(jù)分離方案時,并不是所有數(shù)據(jù)都在 JuiceFS 中,數(shù)據(jù)會優(yōu)先寫入 SSD 盤。
首先選取一個固定的查詢時間范圍,評估 JuiceFS 緩存對性能的影響,測試結(jié)果如下圖:

第一次查詢因為沒有緩存相比后幾次慢一些,從第二次查詢開始 JuiceFS 的查詢性能就比較穩(wěn)定,相比第一次有 70% 左右的性能提升。第四次查詢是間隔上一次 10 分鐘以后進(jìn)行,性能略有波動(可能是受系統(tǒng) page cache 影響),但屬于預(yù)期范圍之內(nèi),第五次又恢復(fù)之前的查詢時間。
然后采用不固定時間范圍進(jìn)行查詢,同時還有數(shù)據(jù)在實時寫入,也就是說每次查詢都會涉及新的數(shù)據(jù),測試結(jié)果如下圖:

跟固定時間范圍的查詢一樣,從第二次查詢開始因為緩存的建立帶來了 78% 左右的性能提升。不同的地方在于第四次查詢因為涉及到查詢新寫入或者合并后的數(shù)據(jù),而 JuiceFS 目前不會在寫入時緩存大文件,會對查詢性能造成一定影響,之后會提供參數(shù)允許緩存寫入數(shù)據(jù)來改善新數(shù)據(jù)的查詢性能。
通過 ClickHouse 的存儲策略可以很簡單地將 SSD 和 JuiceFS 結(jié)合使用,實現(xiàn)性能與成本的兩全方案。從寫入和查詢性能測試的結(jié)果上來看 JuiceFS 完全可以滿足 ClickHouse 的使用場景,用戶不必再擔(dān)心容量問題,在增加少量成本的情況下輕松應(yīng)對未來幾倍的數(shù)據(jù)增長需求。JuiceFS 目前已經(jīng)支持超過 20 家公有云的對象存儲,結(jié)合完全兼容 POSIX 的特性,不需要改動 ClickHouse 任何一行代碼就可以輕松接入云上的對象存儲。
在當(dāng)前越來越強(qiáng)調(diào)云原生的環(huán)境下,存儲計算分離已經(jīng)是大勢所趨。ClickHouse 2021 年的 roadmap 上已經(jīng)明確把存儲計算分離作為了主要目標(biāo),雖然目前 ClickHouse 已經(jīng)支持把數(shù)據(jù)存儲到 S3 上,但這個實現(xiàn)還比較粗糙。未來 JuiceFS 也會與 ClickHouse 社區(qū)緊密合作共同探索存算分離的方向,讓 ClickHouse 更好地識別和支持共享存儲,實現(xiàn)集群伸縮時不需要做任何數(shù)據(jù)拷貝。
如果你對 JuiceFS 感興趣,就訪問 GitHub 開始試用吧:https://github.com/juicedata/juicefs
參考資料:
https://clickhouse.tech/docs/en/development/architecture/
https://github.com/ClickHouse/clickhouse-presentations/blob/master/meetup41/merge_tree.pdf
https://clickhouse.tech/docs/en/engines/table-engines/special/buffer/
https://clickhouse.tech/docs/en/engines/table-engines/mergetree-family/custom-partitioning-key/
https://clickhouse.tech/docs/en/engines/table-engines/mergetree-family/mergetree/#table_engine-mergetree-multiple-volumes
https://github.com/ClickHouse/clickhouse-presentations/blob/master/meetup44/S3.pdf
https://github.com/ClickHouse/ClickHouse/issues/17623


JuiceFS 技術(shù)交流群加入
添加管理員微信:suavesu,備注:公司-姓名,審核后邀請入群。



【社區(qū)案例】如何在大數(shù)據(jù)/機(jī)器學(xué)習(xí)語言 MLSQL 中集成 JuiceFS
巧用符號鏈接遷移 HDFS 數(shù)據(jù),業(yè)務(wù)完全無感知!
如何使用 JuiceFS 在云上優(yōu)化 Kylin 4.0 的存儲性能?
