ClickHouse各種MergeTree的關係與作用

  • 2020 年 3 月 26 日
  • 筆記

在ClickHouse的整個體系裡面,MergeTree表引擎絕對是一等公民,使用ClickHouse就是在使用MergeTree,這種說法一點也不為過。

眾所周知,MergeTree表引擎是一個家族系列,目前整個系列一共包含了14種不同類型的MergeTree,可謂是功能豐富了吧?

但凡事都有兩面性,功能豐富的同時也無疑讓很多朋友犯了難。

這麼多表引擎,它們之間是什麼關係?

我們到底應該使用哪一種表引擎?

今天我就用這篇文章,嘗試回答上述兩個高頻問題。

老師常教導我們要訓練結構化思維,通過抽象、歸納等辦法來分析一個事物,有時候會起到事半功倍的效果。


這麼多表引擎,它們之間是什麼關係?

我們可以使用兩種關係,來理解整個MergeTree系列:

  • 繼承關係

首先,為了便於理解,可以使用繼承關係來看待MergeTree。通過最基礎的MergeTree表引擎,向下派生出6個變種表引擎,如下圖所示

在ClickHouse底層具體的實現方法中,上述7種表引擎的區別主要體現在Merge合併的邏輯部分。如下圖所示,是我簡化後的對象關係:

可以看到,在具體的實現邏輯部分,7種MergeTree共用一個主體,在觸發Merge動作時,調用了各自獨有的合併邏輯。

而除開MergeTree之外的其他6個變種表引擎,它們的Merge合併邏輯,全部是建立在MergeTree基礎之上的,如下圖所示:

它們均繼承於MergeTree的 MergingSortedBlockInputStream。

MergingSortedBlockInputStream 的主要作用,是按照ORDER BY的規則保持新分區數據的有序性。

而其他6種變種MergeTree的合併邏輯,則是在有序的基礎之上 "各有所長",要麼是將排序後相鄰的重複數據消除、亦或是將它們累加匯總。

所以,從繼承關係的角度來理解,我們不僅明白了這7種MergeTree的關係,也進一步明確了一個事實,它們的主要區別在Merge部分的邏輯,所以特殊功能只會在Merge合併時才會觸發。

  • 組合關係

剛才已經介紹了7種MergeTree的關係,餘下的7種是ReplicatedMergeTree系列。

ReplicatedMergeTree與普通的MergeTree又有什麼區別呢? 我們接著看下面這張圖:

圖中的虛線框部分是MergeTree的能力邊界,而ReplicatedMergeTree在它的基礎之上增加了分散式協同的能力。

藉助ZooKeeper的消息日誌廣播,實現了副本實例之間的數據同步功能。

ReplicatedMergeTree系列可以用組合關係來理解,如下圖所示:

當我們為7種MergeTree加上Replicated前綴後,又能組合出7種新的表引擎,這些ReplicatedMergeTree擁有副本協同的能力。


我們到底應該使用哪一種表引擎?

現在回答第二個問題,按照使用的場景劃分,可以將上述14種表引擎大致分成以下6類應用場景:

  • 默認情況

在沒有特殊要求的場合,使用基礎的MergeTree表引擎即可,它不僅擁有高效的性能,也提供了所有MergeTree共有的基礎功能,包括列存、數據分區、分區索引、一級索引、二級索引、TTL、多路徑存儲等等。

與此同時,它也定義了整個MergeTree家族的基調,例如:

ORDER BY 決定了每個分區中數據的排序規則;

PRIMARY KEY 決定了一級索引(primary.idx);

ORDER BY 可以指代PRIMARY KEY, 通常只用聲明ORDER BY 即可。

接下來將要介紹的其他表引擎,除開ReplicatedMergeTree系列外,都是在Merge合併動作時添加了各自獨有的邏輯。

  • 數據去重

通過剛才的說明,大家應該明白,MergeTree的主鍵(PRIMARY KEY)只是用來生成一級索引(primary.idx)的,並沒有唯一性約束這樣的語義。

一些朋友在使用MergeTree的時候,用傳統資料庫的思維來理解MergeTree就會出現問題。

如果業務上不允許數據重複,遇到這類場景就可以使用ReplacingMergeTree,如下圖所示:

ReplacingMergeTree通過ORDER BY,表示判斷唯一約束的條件。當分區合併之時,根據ORDER BY排序後,相鄰重複的數據會被排除。

由此,可以得出幾點結論:

第一,使用ORDER BY作為特殊判斷標識,而不是PRIMARY KEY。關於這一點網上有一些誤傳,但是如果理解了ORDER BYPRIMARY KEY的作用,以及合併邏輯之後,都能夠推理出應該是由ORDER BY決定。

ORDER BY的作用, 負責分區內數據排序;

PRIMARY KEY的作用, 負責一級索引生成;

Merge的邏輯, 分區內數據排序後,找到相鄰的數據,做特殊處理。

第二,只有在觸發合併之後,才能觸發特殊邏輯。以去重為例,在沒有合併的時候,還是會出現重複數據。

第三,只對同一分區內的數據有效。以去重為例,只有屬於相同分區的數據才能去重,跨越不同分區的重複數據不能去重。

上述幾點結論,適用於包含ReplacingMergeTree在內的6種MergeTree,所以後面不在贅述。

  • 預聚合(數據立方體)

有這麼一類場景,它的查詢主題是非常明確的,也就是說聚合查詢的維度欄位是固定,並且沒有明細數據的查詢需求,這類場合就可以使用SummingMergeTree或是AggregatingMergeTree,如下圖所示:

可以看到,在新分區合併後,在同一分區內,ORDER BY條件相同的數據會進行合併。如此一來,首先表內的數據行實現了有效的減少,其次度量值被預先聚合,進一步減少了後續計算開銷。

聚合類MergeTree通常可以和表引擎協同使用,如下圖所示:

可以將物化視圖設置成聚合類MergeTree,將其作為固定主題的查詢表使用。

值得一提的是,通常只有在使用SummingMergeTree或AggregatingMergeTree的時候,才需要同時設置ORDER BYPRIMARY KEY。

顯式的設置PRIMARY KEY,是為了將主鍵和排序鍵設置成不同的值,是進一步優化的體現。

例如某個場景的查詢需求如下:

聚合條件,GROUP BY A,B,C

過濾條件,WHERE A

此時,如下設置將會是一種較優的選擇:

GROUP BY A,B,C

PRIMARY KEY A

BTW,如果ORDER BYPRIMARY KEY不同,PRIMARY KEY必須是ORDER BY的前綴(為了保證分區內數據和主鍵的有序性)。

  • 數據更新

數據的更新在ClickHouse中有多種實現手段,例如按照分區Partition重新寫入、使用Mutation的DELETE和UPDATE查詢。

使用CollapsingMergeTree或VersionedCollapsingMergeTree也能實現數據更新,這是一種使用標記位,以增代刪的數據更新方法,如下圖所示:

通過增加一個標誌欄位(例如圖中的sigh欄位),作為數據有效性的判斷依據。

可以看到,在新分區合併後,在同一分區內,ORDER BY條件相同的數據,其標誌值為1和-1的數據行會進行抵消。

下圖是另外一種便於理解的視角,就如同擠壓瓦楞紙一般,數據被抵消了:

CollapsingMergeTree和VersionedCollapsingMergeTree的區別又是什麼呢?

CollapsingMergeTree對數據寫入的順序是敏感的,它要求標誌位需要按照正確的順序排序。例如按照1,-1的寫入順序是正確的; 而如果按照-1,1的錯誤順序寫入,CollapsingMergeTree就無法正確抵消。

試想,如果在一個多執行緒並行的寫入場景,我們是無法保證這種順序寫入的,此時就需要使用VersionedCollapsingMergeTree了。

VersionedCollapsingMergeTree在CollapsingMergeTree基礎之上,額外要求指定一個version欄位,在分區Merge合併時,它會自動將version欄位追加到ORERY BY的末尾,從而保證了標誌位的有序性。

ENGINE = VersionedCollapsingMergeTree(sign,ver)ORDER BY id//等效於ORDER BY id,ver
  • 監控集成

GraphiteMergeTree可以與Graphite集成,如果你使用了Graphite作為系統的運行監控系統, 則可以通過GraphiteMergeTree存儲指標數據,加速查詢性能、降低存儲成本。

  • 高可用

Replicated* 擁有數據副本的能力,如下圖所示:

結合剛才的5類場景,如果進一步需要高可用的需求,選擇一種MergeTree和Replicated組合即可,例如ReplicatedMergeTree、ReplicatedReplacingMergeTree等等。

讀完這篇文章,你是否對ClickHouse的MergeTree家族有了更深刻的認識呢?如果仍有疑問,歡迎觀看我在騰訊雲做的ClickHouse科普直播,地址如下:

ClickHouse的前世今生-直播回放

在影片中我也專門介紹了ClickHouse的表引擎部分。