[toc] >VersionedCollapsingMergeTree引擎繼承自MergeTree並將摺疊行的邏輯添加到合併數據部分的演算法中。VersionedCollapsingMergeTree用於相同的目的摺疊樹但使用不同的摺疊演算法,允許以多個線程的任何順序插入數據。特別是,Version列有 ...
目錄
VersionedCollapsingMergeTree引擎繼承自MergeTree並將摺疊行的邏輯添加到合併數據部分的演算法中。VersionedCollapsingMergeTree用於相同的目的摺疊樹但使用不同的摺疊演算法,允許以多個線程的任何順序插入數據。特別是,Version列有助於正確摺疊行,即使它們以錯誤的順序插入。相比之下,CollapsingMergeTree只允許嚴格連續插入。
VersionedCollapsingMergeTree引擎的作用如下:
- 允許快速寫入不斷變化的對象狀態。
- 刪除後臺中的舊對象狀態。 這顯著降低了存儲體積。
建表語法
CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
(
name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1],
name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2],
...
) ENGINE = VersionedCollapsingMergeTree(sign, version)
[PARTITION BY expr]
[ORDER BY expr]
[SAMPLE BY expr]
[SETTINGS name=value, ...]
針對於VersionedCollapsingMergeTree(sign, version)兩個特殊的參數。
sign — 指定行類型的列名:1是一個“state”行,-1是一個“cancel”行列數據類型應為Int8.
version — 指定對象狀態版本的列名。列數據類型應為UInt*.
使用場景
考慮一種情況,您需要為某個對象保存不斷變化的數據。對於一個對象有一行,併在發生更改時更新該行是合理的。但是,對於資料庫管理系統來說,更新操作非常昂貴且速度很慢,因為它需要重寫存儲中的數據。如果需要快速寫入數據,則不能接受更新,但可以按如下順序將更改寫入對象。使用 Sign 列寫入行時。如果Sign=1這意味著該行是一個對象的狀態(讓我們把它稱為“state”行)。如果Sign=-1它指示具有相同屬性的對象的狀態的取消(讓我們稱之為“cancel”行)。 還可以使用 Version 列,它應該用單獨的數字標識對象的每個狀態。
例如,我們要計算用戶在某個網站上訪問了多少頁面以及他們在那裡的時間。在某個時間點,我們用用戶活動的狀態寫下麵的行:
┌──────────────UserID─┬─PageViews─┬─Duration─┬─Sign─┐
│ 4324182021466249494 │ 5 │ 146 │ 1 │
└─────────────────────┴───────────┴──────────┴──────┘
在稍後的某個時候,我們註冊用戶活動的變化,並用以下兩行寫入它。
┌──────────────UserID─┬─PageViews─┬─Duration─┬─Sign─┐
│ 4324182021466249494 │ 5 │ 146 │ -1 │
│ 4324182021466249494 │ 6 │ 185 │ 1 │
└─────────────────────┴───────────┴──────────┴──────┘
第一行取消對象(用戶)的先前狀態。它應該複製已取消狀態的所有欄位,除了Sign。
第二行包含當前狀態。
因為我們只需要用戶活動的最後一個狀態,所以需要刪除,摺疊對象的無效(舊)狀態。VersionedCollapsingMergeTree會在在合併數據部分時執行此操作。
最終摺疊之後的結果如下。
┌──────────────UserID─┬─PageViews─┬─Duration─┬─Sign─┬─Version─┐
│ 4324182021466249494 │ 5 │ 146 │ 1 │ 1 |
│ 4324182021466249494 │ 5 │ 146 │ -1 │ 1 |
└─────────────────────┴───────────┴──────────┴──────┴─────────┘
對於使用VersionedCollapsingMergeTree有下麵三個需要註意的點。
- 寫入數據的程式應該記住對象的狀態以取消它。該“cancel”字元串應該是“state”與相反的字元串Sign。這增加了存儲的初始大小,但允許快速寫入數據。
- 列中長時間增長的數組由於寫入負載而降低了引擎的效率。數據越簡單,效率就越高。
- SELECT結果很大程度上取決於對象變化歷史的一致性。準備插入數據時要準確。不一致的數據將導致不可預測的結果,例如會話深度等非負指標的負值。
合併演算法
合併演算法主要是下麵兩個。
- 當ClickHouse合併數據部分時,它會刪除具有相同主鍵和版本但Sign值不同的一對行.行的順序並不重要。
- 當ClickHouse插入數據時,它會按主鍵對行進行排序。如果Version列不在主鍵中,ClickHouse將其隱式添加到主鍵作為最後一個欄位並使用它進行排序。
ClickHouse不保證具有相同主鍵的所有行都將位於相同的結果數據部分中,甚至位於相同的物理伺服器上。對於寫入數據和隨後合併數據部分都是如此。此外,ClickHouse流程SELECT具有多個線程的查詢,並且無法預測結果中的行順序。這意味著,如果有必要從VersionedCollapsingMergeTree表中得到完全“collapsed”的數據,聚合是必需的。
也就是說ClickHouse並不保證查詢出來的數據一定是經過合併摺疊的。如果要保證一定經過摺疊合併,需要查詢的時候使用GROUP BY和聚合函數。
要計算數量,使用sum(Sign)而不是count()。要計算的東西的總和,使用sum(Sign * x)而不是sum(x),並添加HAVING sum(Sign) > 0。可以在一定程度上避免數據未摺疊導致的數據問題。
如果您需要手動摺疊合併,但是,如果沒有聚合(例如,要檢查是否存在其最新值與某些條件匹配的行),則可以使用FINAL修飾FROM條件這種方法效率低下,不應與大型表一起使用。
使用例子、
示例數據:
┌──────────────UserID─┬─PageViews─┬─Duration─┬─Sign─┬─Version─┐
│ 4324182021466249494 │ 5 │ 146 │ 1 │ 1 |
│ 4324182021466249494 │ 5 │ 146 │ -1 │ 1 |
│ 4324182021466249494 │ 6 │ 185 │ 1 │ 2 |
└─────────────────────┴───────────┴──────────┴──────┴─────────┘
創建表:
CREATE TABLE UAct
(
UserID UInt64,
PageViews UInt8,
Duration UInt8,
Sign Int8,
Version UInt8
)
ENGINE = VersionedCollapsingMergeTree(Sign, Version)
ORDER BY UserID
插入數據:
INSERT INTO UAct VALUES (4324182021466249494, 5, 146, 1, 1)
INSERT INTO UAct VALUES (4324182021466249494, 5, 146, -1, 1),(4324182021466249494, 6, 185, 1, 2)
我們用兩個INSERT查詢以創建兩個不同的數據部分。
如果我們使用單個查詢插入數據,ClickHouse將創建一個數據部分,並且永遠不會執行任何合併。
獲取數據:
SELECT * FROM UAct
┌──────────────UserID─┬─PageViews─┬─Duration─┬─Sign─┬─Version─┐
│ 4324182021466249494 │ 5 │ 146 │ 1 │ 1 │
└─────────────────────┴───────────┴──────────┴──────┴─────────┘
┌──────────────UserID─┬─PageViews─┬─Duration─┬─Sign─┬─Version─┐
│ 4324182021466249494 │ 5 │ 146 │ -1 │ 1 │
│ 4324182021466249494 │ 6 │ 185 │ 1 │ 2 │
└─────────────────────┴───────────┴──────────┴──────┴─────────┘
我們在這裡看到了什麼,摺疊的合併部分在哪裡?我們使用兩個創建了兩個數據部分INSERT查詢。該SELECT查詢是在兩個線程中執行的,結果是行的隨機順序。由於數據部分尚未合併,因此未發生摺疊合併。 ClickHouse在我們無法預測的未知時間點合併數據部分。
這就是為什麼我們需要聚合:
SELECT
UserID,
sum(PageViews * Sign) AS PageViews,
sum(Duration * Sign) AS Duration,
Version
FROM UAct
GROUP BY UserID, Version
HAVING sum(Sign) > 0
┌──────────────UserID─┬─PageViews─┬─Duration─┬─Version─┐
│ 4324182021466249494 │ 6 │ 185 │ 2 │
└─────────────────────┴───────────┴──────────┴─────────┘
如果我們不需要聚合,並希望強制摺疊,我們可以使用 FINAL 修飾符 FROM 條款
SELECT * FROM UAct FINAL
┌──────────────UserID─┬─PageViews─┬─Duration─┬─Sign─┬─Version─┐
│ 4324182021466249494 │ 6 │ 185 │ 1 │ 2 │
└─────────────────────┴───────────┴──────────┴──────┴─────────┘
資料分享
參考文章
- 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/17495702.html
公眾號:張飛的豬大數據分享,不定期分享大數據學習的總結和相關資料,歡迎關註。
個人網站"張飛的豬編程工作室"鏈接: https://zhangfeidezhu.com