該引擎繼承於MergeTree,併在數據塊合併演算法中添加了摺疊行的邏輯。CollapsingMergeTree會非同步的刪除(摺疊)這些除了特定列Sign有1和-1的值以外,其餘所有欄位的值都相等的成對的行。沒有成對的行會被保留。因此,該引擎可以顯著的降低存儲量並提高SELECT查詢效率。 簡單來說就 ...
目錄
該引擎繼承於MergeTree,併在數據塊合併演算法中添加了摺疊行的邏輯。CollapsingMergeTree會非同步的刪除(摺疊)這些除了特定列Sign有1和-1的值以外,其餘所有欄位的值都相等的成對的行。沒有成對的行會被保留。因此,該引擎可以顯著的降低存儲量並提高SELECT查詢效率。
簡單來說就是,clickhouse會自動的合併有效和無效的數據,減少數據存儲,並減少update所產生的性能消耗。具體的邏輯,下麵介紹。
建表
CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
(
name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1],
name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2],
...
) ENGINE = CollapsingMergeTree(sign)
[PARTITION BY expr]
[ORDER BY expr]
[SAMPLE BY expr]
[SETTINGS name=value, ...]
sign — 類型列的名稱:1是«狀態»行,也就是最後的有效行,-1是«取消»行,也就是無效行。列數據類型 — Int8。
創建CollapsingMergeTree表時,需要與創建 MergeTree 表時相同的子句。
摺疊
數據
考慮你需要為某個對象保存不斷變化的數據的情景。似乎為一個對象保存一行記錄併在其發生任何變化時更新記錄是合乎邏輯的,但是更新操作對DBMS來說是昂貴且緩慢的,因為它需要重寫存儲中的數據。如果你需要快速的寫入數據,則更新操作是不可接受的,但是你可以按下麵的描述順序地更新一個對象的變化。
在寫入行的時候使用特定的列Sign。如果Sign=1則表示這一行是對象的狀態,我們稱之為«狀態»行。如果Sign=-1則表示是對具有相同屬性的狀態行的取消,我們稱之為«取消»行。
例如,我們想要計算用戶在某個站點訪問的頁面頁面數以及他們在那裡停留的時間。在某個時候,我們將用戶的活動狀態寫入下麵這樣的行。
┌──────────────UserID─┬─PageViews─┬─Duration─┬─Sign─┐
│ 4324182021466249494 │ 5 │ 146 │ 1 │
└─────────────────────┴───────────┴──────────┴──────┘
一段時間後,我們寫入下麵的兩行來記錄用戶活動的變化。
┌──────────────UserID─┬─PageViews─┬─Duration─┬─Sign─┐
│ 4324182021466249494 │ 5 │ 146 │ -1 │
│ 4324182021466249494 │ 6 │ 185 │ 1 │
└─────────────────────┴───────────┴──────────┴──────┘
第一行取消了這個對象(用戶)的狀態。它需要複製被取消的狀態行的所有除了Sign的屬性。
第二行包含了當前的狀態。因為我們只需要用戶活動的最後狀態,這些行可以在摺疊對象的失效(老的)狀態的時候被刪除。CollapsingMergeTree會在合併數據片段的時候做這件事。
┌──────────────UserID─┬─PageViews─┬─Duration─┬─Sign─┐
│ 4324182021466249494 │ 5 │ 146 │ 1 │
│ 4324182021466249494 │ 5 │ 146 │ -1 │
└─────────────────────┴───────────┴──────────┴──────┘
這種方法的特殊屬性
- 寫入的程式應該記住對象的狀態從而可以取消它。«取消»字元串應該是«狀態»字元串的複製,除了相反的Sign。它增加了存儲的初始數據的大小,但使得寫入數據更快速。
- 由於寫入的負載,列中長的增長陣列會降低引擎的效率。數據越簡單,效率越高。
- SELECT的結果很大程度取決於對象變更歷史的一致性。在準備插入數據時要準確。在不一致的數據中會得到不可預料的結果,例如,像會話深度這種非負指標的負值。
演算法
當ClickHouse合併數據片段時,每組具有相同主鍵的連續行被減少到不超過兩行,一行Sign=1(«狀態»行),另一行Sign=-1(«取消»行),換句話說,數據項被摺疊了。
對每個結果的數據部分ClickHouse保存的演算法:
- 如果«取消»和«狀態»行數量相同,並且最後一行«狀態»行,保留第一個«取消»和最後一個«狀態»行。
- 如果«狀態»行比«取消»行多一個或一個以上,保留最後一個«狀態»行。
- 如果«取消»行比«狀態»行多一個或一個以上,保留第一個«取消»行。
- 沒有行,在其他所有情況下。合併會繼續,但ClickHouse會把此情況視為邏輯錯誤並將其記錄在服務日誌中。這個錯誤會在相同的數據被插入超過一次時出現。
因此,摺疊不應該改變統計數據的結果。變化逐漸地被摺疊,因此最終幾乎每個對象都只剩下了最後的狀態。
Sign是必須的因為合併演算法不保證所有有相同主鍵的行都會在同一個結果數據片段中,甚至是在同一臺物理伺服器上。ClickHouse用多線程來處理SELECT請求,所以它不能預測結果中行的順序。如果要從CollapsingMergeTree表中獲取完全«摺疊»後的數據,則需要聚合。
要完成摺疊,請使用GROUP BY子句和用於處理符號的聚合函數編寫請求。例如,要計算數量,使用sum(Sign)而不是 count()。要計算某物的總和,使用sum(Sign * x)而不是sum(x),並添加HAVING sum(Sign) > 0子句。
聚合體count,sum和avg可以用這種方式計算。如果一個對象至少有一個未被摺疊的狀態,則可以計算uniq聚合。min和 max聚合無法計算,因為CollaspingMergeTree不會保存摺疊狀態的值的歷史記錄。
如果你需要在不進行聚合的情況下獲取數據(例如,要檢查是否存在最新值與特定條件匹配的行),你可以在 FROM 從句中使用 FINAL 修飾符。這種方法顯然是更低效的。
# 示例:
┌──────────────UserID─┬─PageViews─┬─Duration─┬─Sign─┐
│ 4324182021466249494 │ 5 │ 146 │ 1 │
│ 4324182021466249494 │ 5 │ 146 │ -1 │
│ 4324182021466249494 │ 6 │ 185 │ 1 │
└─────────────────────┴───────────┴──────────┴──────┘
# 建表:
CREATE TABLE UAct
(
UserID UInt64,
PageViews UInt8,
Duration UInt8,
Sign Int8
)
ENGINE = CollapsingMergeTree(Sign)
ORDER BY UserID
# 插入數據:
INSERT INTO UAct VALUES (4324182021466249494, 5, 146, 1)
INSERT INTO UAct VALUES (4324182021466249494, 5, 146, -1),(4324182021466249494, 6, 185, 1)
#我們使用兩次INSERT請求來創建兩個不同的數據片段。如果我們使用一個請求插入數據,ClickHouse只會創建一個數據片段且不會執行任何合併操作。
#獲取數據:
SELECT * FROM UAct
┌──────────────UserID─┬─PageViews─┬─Duration─┬─Sign─┐
│ 4324182021466249494 │ 5 │ 146 │ -1 │
│ 4324182021466249494 │ 6 │ 185 │ 1 │
└─────────────────────┴───────────┴──────────┴──────┘
┌──────────────UserID─┬─PageViews─┬─Duration─┬─Sign─┐
│ 4324182021466249494 │ 5 │ 146 │ 1 │
└─────────────────────┴───────────┴──────────┴──────┘
#我們看到了什麼,哪裡有摺疊?
#通過兩個 INSERT 請求,我們創建了兩個數據片段。
#SELECT請求在兩個線程中被執行,我們得到了隨機順序的行。
#沒有發生摺疊是因為還沒有合併數據片段。
#ClickHouse 在一個我們無法預料的未知時刻合併數據片段。
#因此我們需要聚合:
SELECT
UserID,
sum(PageViews * Sign) AS PageViews,
sum(Duration * Sign) AS Duration
FROM UAct
GROUP BY UserID
HAVING sum(Sign) > 0
┌──────────────UserID─┬─PageViews─┬─Duration─┐
│ 4324182021466249494 │ 6 │ 185 │
└─────────────────────┴───────────┴──────────┘
# 如果我們不需要聚合併想要強制進行摺疊,我們可以在 FROM 從句中使用 FINAL 修飾語。
SELECT * FROM UAct FINAL
┌──────────────UserID─┬─PageViews─┬─Duration─┬─Sign─┐
│ 4324182021466249494 │ 6 │ 185 │ 1 │
└─────────────────────┴───────────┴──────────┴──────┘
# 這種查詢數據的方法是非常低效的。不要在大表中使用它。
資料分享
參考文章
- ClickHouse(01)什麼是ClickHouse,ClickHouse適用於什麼場景
- ClickHouse(02)ClickHouse架構設計介紹概述與ClickHouse數據分片設計
- ClickHouse(03)ClickHouse怎麼安裝和部署
- ClickHouse(04)如何搭建ClickHouse集群
- ClickHouse(05)ClickHouse數據類型詳解
- ClickHouse(06)ClickHouse建表語句DDL詳細解析
- ClickHouse(07)ClickHouse資料庫引擎解析
- ClickHouse(08)ClickHouse表引擎概況
- ClickHouse(09)ClickHouse合併樹MergeTree家族表引擎之MergeTree詳細解析
- ClickHouse(10)ClickHouse合併樹MergeTree家族表引擎之ReplacingMergeTree詳細解析
- ClickHouse(11)ClickHouse合併樹MergeTree家族表引擎之SummingMergeTree詳細解析
本文來自博客園,作者:張飛的豬,轉載請註明原文鏈接:https://www.cnblogs.com/the-pig-of-zf/p/17166258.html
公眾號:張飛的豬大數據分享,不定期分享大數據學習的總結和相關資料,歡迎關註。
個人網站"張飛的豬編程工作室"鏈接: https://zhangfeidezhu.com