ClickHouse 存算分離架構探索

JuiceFS發表於2021-10-19

背景

ClickHouse 作為開源 OLAP 引擎,因其出色的效能表現在大資料生態中得到了廣泛的應用。區別於 Hadoop 生態元件通常依賴 HDFS 作為底層的資料儲存,ClickHouse 使用本地盤來自己管理資料,官方推薦使用 SSD 作為儲存介質來提升效能。但受限於本地盤的容量上限以及 SSD 盤的價格,使用者很難在容量、成本和效能這三者之間找到一個好的平衡。JuiceFS 的某個客戶近期就遇到了這樣的難題,希望將 ClickHouse 中的溫冷資料從 SSD 盤遷移到更大容量、更低成本的儲存介質,更好地支撐業務查詢更長時間資料的需求。

JuiceFS 是基於物件儲存實現並完全相容 POSIX 的開源分散式檔案系統,同時 JuiceFS 的資料快取特性可以智慧管理查詢熱點資料,非常適合作為 ClickHouse 的儲存系統,下面將詳細介紹這個方案。

MergeTree 儲存格式簡介

在介紹具體方案之前先簡單瞭解一下 MergeTree 的儲存格式。MergeTree 是 ClickHouse 最主要使用的儲存引擎,當建立表時可以通過 PARTITION BY 語句指定以某一個或多個欄位作為分割槽欄位,資料在磁碟上的目錄結構類似如下形式:

$ 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 是分割槽的名稱,1 是最小的資料塊編號,3 是最大的資料塊編號,0 是 MergeTree 的深度。可以看到 202102 這個分割槽不止一個目錄,這是因為 ClickHouse 每次在寫入的時候都會生成一個新的目錄,並且一旦寫入以後就不會修改(immutable)。每一個目錄稱作一個「part」,當 part 逐漸變多以後 ClickHouse 會在後臺對多個 part 進行合併(compaction),通常的建議是不要保留過多 part,否則會影響查詢效能。

每個 part 目錄內部又由很多大大小小的檔案組成,這裡面既有資料,也有一些元資訊,一個典型的目錄結構如下所示:

$ 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:這個檔案包含的是主鍵資訊,但不是當前 part 全部行的主鍵,預設會按照 8192 這個區間來儲存,也就是每 8192 行儲存一次主鍵。
ColumnA.bin:這是壓縮以後的某一列的資料,ColumnA 只是這一列的代稱,實際情況會是真實的列名。壓縮是以 block 作為最小單位,每個 block 的大小從 64KiB 到 1MiB 不等。
ColumnA.mrk:這個檔案儲存的是對應的 ColumnA.bin 檔案中每個 block 壓縮後和壓縮前的偏移。
partition.dat:這個檔案包含的是經過分割槽表示式計算以後的分割槽 ID。
minmax_ColumnC.idx:這個檔案包含的是分割槽欄位對應的原始資料的最小值和最大值。

基於 JuiceFS 的存算分離方案

因為 JuiceFS 完全相容 POSIX,所以可以把 JuiceFS 掛載的檔案系統直接作為 ClickHouse 的磁碟來使用。這種方案下資料會直接寫入 JuiceFS,結合為 ClickHouse 節點配置的快取盤,查詢時涉及的熱資料會自動快取在 ClickHouse 節點本地。整體方案如下圖所示。

ClickHouse 在寫入時會產生大量的小檔案,因此如果寫入壓力較大這個方案對寫入和查詢效能都會有一定影響。建議在寫入資料時增大寫入快取,儘量一次寫入更多資料來避免這個小檔案過多的問題。最簡單的做法是使用 ClickHouse 的 Buffer 表,基本上不需要修改應用程式碼就可以解決小檔案過多的問題,適合當 ClickHouse 當機時允許少量資料丟失的場景。這樣做的好處是儲存和計算完全分離,ClickHouse 節點完全無狀態,如果節點故障可以很快恢復,不涉及任何資料拷貝。未來可以讓 ClickHouse 感知到底層儲存是共享的,實現自動的無資料拷貝遷移。

同時由於 ClickHouse 通常應用在實時分析場景,這個場景對於資料實時更新的要求比較高,在分析時也需要經常性地查詢新資料。因此資料具有比較明顯的冷熱特徵,即一般新資料是熱資料,隨著時間推移歷史資料逐漸變為冷資料。利用 ClickHouse 的儲存策略(storage policy)來配置多塊磁碟,通過一定條件可以實現自動遷移冷資料到 JuiceFS。整體方案如下圖所示。

這個方案中資料會先寫入本地磁碟,當滿足一定條件時 ClickHouse 的後臺執行緒會非同步把資料從本地磁碟遷移到 JuiceFS 上。和第一個方案一樣,查詢時也會自動快取熱資料。注意圖中為了區分寫和讀因此畫了兩塊磁碟,實際使用中沒有這個限制,可以使用同一個盤。雖然這個方案不是完全的儲存計算分離,但是可以滿足對寫入效能要求特別高的場景需求,也保留一定的儲存資源彈性伸縮能力。下面會詳細介紹這個方案在 ClickHouse 中如何配置。

ClickHouse 支援配置多塊磁碟用於資料儲存,下面是示例的配置檔案:

<storage_configuration>
    <disks>
        <jfs>
            <path>/jfs</path>
        </jfs>
    </disks>
</storage_configuration>

上面的 /jfs 目錄即是 JuiceFS 檔案系統掛載的路徑。在把以上配置新增到 ClickHouse 的配置檔案中,併成功掛載 JuiceFS 檔案系統以後,就可以通過 MOVE PARTITION 命令將某個 partition 移動到 JuiceFS 上,例如:

ALTER TABLE test MOVE PARTITION 'xxx' TO DISK 'jfs';

當然這種手動移動的方式只是用於測試,ClickHouse 支援通過配置儲存策略的方式來將資料自動從某個磁碟移動到另一個磁碟。下面是示例的配置檔案:

<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 是預設的 SSD 盤,名為 cold 的 volume 即是上一步 disks 中定義的 JuiceFS 盤。這些 volume 在配置檔案中的順序很重要,資料會首先儲存到第一個 volume 中,而 max_data_part_size_bytes 這個配置表示當資料 part 超過指定的大小時(示例中是 1GiB)自動從當前 volume 移動到下一個 volume,也就是把資料從 SSD 盤移動到 JuiceFS。最後的 move_factor 配置表示當 SSD 盤的磁碟容量超過 90% 時也會觸發資料移動到 JuiceFS。

最後在建立表時需要顯式指定要用到的儲存策略:

CREATE TABLE test (
  ...
) ENGINE = MergeTree
...
SETTINGS storage_policy = 'hot_and_cold';

當滿足資料移動的條件時,ClickHouse 就會啟動後臺執行緒去執行移動資料的操作,預設會有 8 個執行緒同時工作,這個執行緒數量可以通過 background_move_pool_size配置調整。

除了配置儲存策略以外,還可以在建立表時通過 TTL 將超過一段時間的資料移動到 JuiceFS 上,例如:

CREATE TABLE test (
  d DateTime,
  ...
) ENGINE = MergeTree
...
TTL d + INTERVAL 1 DAY TO DISK 'jfs'
SETTINGS storage_policy = 'hot_and_cold';

上面的例子是將超過 1 天的資料移動到 JuiceFS 上,結合儲存策略一起可以非常靈活地管理資料的生命週期。

寫入效能測試

採用冷熱資料分離方案以後資料並不會直接寫入 JuiceFS,而是先寫入 SSD 盤,再通過後臺執行緒非同步遷移到 JuiceFS 上。但是我們希望直接評估不同儲存介質在寫資料的場景有多大的效能差異,因此這裡在測試寫入效能時沒有配置冷熱資料分離的儲存策略,而是讓 ClickHouse 直接寫入不同的儲存介質。

具體測試方法是將真實業務中的某一張 ClickHouse 表作為資料來源,然後使用 INSERT INTO 語句批量插入千萬級行數的資料,比較直接寫入 SSD 盤、JuiceFS 以及物件儲存的吞吐。最終的測試結果如下圖:

以 SSD 盤作為基準,可以看到 JuiceFS 的寫入效能與 SSD 盤有 30% 左右的效能差距,但是相比物件儲存有 11 倍的效能提升。這裡 JuiceFS 的測試中開啟了 writeback 選項,這是因為 ClickHouse 在寫入時每個 part 會產生大量的小檔案(KiB 級),客戶端採用非同步寫入的方式能明顯提升效能,同時大量的小檔案對於查詢效能也會造成一定影響。

在瞭解了直接寫入不同介質的效能以後,接下來測試冷熱資料分離方案的寫入效能。經過實際業務測試,基於 JuiceFS 的冷熱資料分離方案表現穩定,因為新資料都是直接寫入 SSD 盤,因此寫入效能與上面測試中的 SSD 盤效能相當。SSD 盤上的資料可以很快遷移到 JuiceFS 上,在 JuiceFS 上對資料 part 進行合併也都是沒有問題的。

查詢效能測試

查詢效能測試使用真實業務中的資料,並選取幾個典型的查詢場景進行測試。其中 q1-q4 是掃描全表的查詢,q5-q7 是命中主鍵索引的查詢。測試結果如下圖:

可以看到 JuiceFS 與 SSD 盤的查詢效能基本相當,平均差異在 6% 左右,但是物件儲存相比 SSD 盤有 1.4 至 30 倍的效能下降。得益於 JuiceFS 高效能的後設資料操作以及本地快取特性,可以自動將查詢請求需要的熱資料快取在 ClickHouse 節點本地,大幅提升了 ClickHouse 的查詢效能。需要注意的是以上測試中物件儲存是通過 ClickHouse 的 S3 磁碟型別進行訪問,這種方式只有資料是儲存在物件儲存上,後設資料還是在本地磁碟。如果通過類似 S3FS 的方式把物件儲存掛載到本地,效能會有進一步的下降。

在完成基礎的查詢效能測試以後,接下來測試冷熱資料分離方案下的查詢效能。區別於前面的測試,當採用冷熱資料分離方案時,並不是所有資料都在 JuiceFS 中,資料會優先寫入 SSD 盤。

首先選取一個固定的查詢時間範圍,評估 JuiceFS 快取對效能的影響,測試結果如下圖:

跟固定時間範圍的查詢一樣,從第二次查詢開始因為快取的建立帶來了 78% 左右的效能提升。不同的地方在於第四次查詢因為涉及到查詢新寫入或者合併後的資料,而 JuiceFS 目前不會在寫入時快取大檔案,會對查詢效能造成一定影響,之後會提供引數允許快取寫入資料來改善新資料的查詢效能。

總結

通過 ClickHouse 的儲存策略可以很簡單地將 SSD 和 JuiceFS 結合使用,實現效能與成本的兩全方案。從寫入和查詢效能測試的結果上來看 JuiceFS 完全可以滿足 ClickHouse 的使用場景,使用者不必再擔心容量問題,在增加少量成本的情況下輕鬆應對未來幾倍的資料增長需求。JuiceFS 目前已經支援超過 20 家公有云的物件儲存,結合完全相容 POSIX 的特性,不需要改動 ClickHouse 任何一行程式碼就可以輕鬆接入雲上的物件儲存。

展望

在當前越來越強調雲原生的環境下,儲存計算分離已經是大勢所趨。ClickHouse 2021 年的 roadmap 上已經明確把儲存計算分離作為了主要目標,雖然目前 ClickHouse 已經支援把資料儲存到 S3 上,但這個實現還比較粗糙。未來 JuiceFS 也會與 ClickHouse 社群緊密合作共同探索存算分離的方向,讓 ClickHouse 更好地識別和支援共享儲存,實現叢集伸縮時不需要做任何資料拷貝。

其他:
Elasticsearch 儲存成本省 60%,稿定科技乾貨分享

相關文章