ClickHouse(13)ClickHouse合併樹MergeTree家族表引擎之CollapsingMergeTree詳細解析

来源:https://www.cnblogs.com/the-pig-of-zf/archive/2023/02/28/17166258.html
-Advertisement-
Play Games

該引擎繼承於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 │
└─────────────────────┴───────────┴──────────┴──────┘

這種方法的特殊屬性

  1. 寫入的程式應該記住對象的狀態從而可以取消它。«取消»字元串應該是«狀態»字元串的複製,除了相反的Sign。它增加了存儲的初始數據的大小,但使得寫入數據更快速。
  2. 由於寫入的負載,列中長的增長陣列會降低引擎的效率。數據越簡單,效率越高。
  3. SELECT的結果很大程度取決於對象變更歷史的一致性。在準備插入數據時要準確。在不一致的數據中會得到不可預料的結果,例如,像會話深度這種非負指標的負值。

演算法

當ClickHouse合併數據片段時,每組具有相同主鍵的連續行被減少到不超過兩行,一行Sign=1(«狀態»行),另一行Sign=-1(«取消»行),換句話說,數據項被摺疊了。

對每個結果的數據部分ClickHouse保存的演算法

  1. 如果«取消»和«狀態»行數量相同,並且最後一行«狀態»行,保留第一個«取消»和最後一個«狀態»行。
  2. 如果«狀態»行比«取消»行多一個或一個以上,保留最後一個«狀態»行。
  3. 如果«取消»行比«狀態»行多一個或一個以上,保留第一個«取消»行。
  4. 沒有行,在其他所有情況下。合併會繼續,但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經典中文文檔分享

參考文章

本文來自博客園,作者:張飛的豬,轉載請註明原文鏈接:https://www.cnblogs.com/the-pig-of-zf/p/17166258.html

公眾號:張飛的豬大數據分享,不定期分享大數據學習的總結和相關資料,歡迎關註。

個人網站"張飛的豬編程工作室"鏈接: https://zhangfeidezhu.com


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1.年輕代存活的對象太多,老年代了放不下 01.示例代碼 public class DemoTest1 { public static void main(String[] args) { byte[] array1 = new byte[4 * 1024 * 1024]; array1 = nul ...
  • 一:背景 1. 講故事 前段時間微信上有一位老朋友找到我,說他的程式跑著跑著記憶體會突然爆高,有時候會下去,有什麼會下不去,懷疑是不是某些情況下存在記憶體泄露,讓我幫忙分析一下,其實記憶體泄露方面的問題還是比較好解決的,看過這個dump之後覺得還是有一定的分享價值,拿出來和大家分享一下吧。 二:WinDb ...
  • 概述 代理模式就是給某一個對象提供一個代理,並由代理對象控制對原對象的引用。在一些情況下,一個客戶不想或者不能直接引用一個對象,而代理對象可以在客戶端和目標對象之間起到中介的作用。例如電腦桌面的快捷方式就是一個代理對象,快捷方式是它所引用的程式的一個代理。 代理模式一般又分為安全代理,虛擬代理 ,遠 ...
  • 1.系統是預設安裝的,查看系統是否安裝logrotate centos rpm -ql logrotate /etc/cron.daily/logrotate /etc/logrotate.conf /etc/logrotate.d /etc/rwtab.d/logrotate /usr/sbin/ ...
  • 1.IO的概念 IO簡單來講就是對輸入輸出設備的簡化表達形式 單片機中各種介面,進行數據流的傳輸 從磁碟中讀取數據至記憶體,又或者從記憶體中寫入磁碟 編程中的IO 此時的IO其應用程式的運行態,即進程 》IO調用 特別強調的是我們的應用程式其實並不存在實質的IO過程,真正的IO過程是操作系統的事情, 這 ...
  • 問題產生: 作者最近在搭建Hadoop+Hive集群時,將NameNode、DataNode、Rm全部部署到一臺物理機上,查詢量較大時連接掛掉。 問題定位: 使用JPS命令查看Metastore服務正常運行,hive2--Runjar掛掉。重啟之後,過段時間又會掛掉。 Linux 內核有個機制叫OO ...
  • 下載 官網下載地址:http://nginx.org/en/download.html Mainline version 開發版Stable version 穩定版Legacy versions 歷史版 滑鼠移動到你要選擇的版本超鏈接上點右鍵 複製鏈接地址 下載:wget http://nginx. ...
  • 編寫hello world腳本 #!/bin/bash # 編寫hello world腳本 echo "Hello World!" ##通過位置變數創建 Linux 系統賬戶及密碼 #!/bin/bash # 通過位置變數創建 Linux 系統賬戶及密碼 #$1 是執行腳本的第一個參數,$2 是執行 ...
一周排行
    -Advertisement-
    Play Games
  • 概述:在C#中,++i和i++都是自增運算符,其中++i先增加值再返回,而i++先返回值再增加。應用場景根據需求選擇,首碼適合先增後用,尾碼適合先用後增。詳細示例提供清晰的代碼演示這兩者的操作時機和實際應用。 在C#中,++i 和 i++ 都是自增運算符,但它們在操作上有細微的差異,主要體現在操作的 ...
  • 上次發佈了:Taurus.MVC 性能壓力測試(ap 壓測 和 linux 下wrk 壓測):.NET Core 版本,今天計劃準備壓測一下 .NET 版本,來測試並記錄一下 Taurus.MVC 框架在 .NET 版本的性能,以便後續持續優化改進。 為了方便對比,本文章的電腦環境和測試思路,儘量和... ...
  • .NET WebAPI作為一種構建RESTful服務的強大工具,為開發者提供了便捷的方式來定義、處理HTTP請求並返迴響應。在設計API介面時,正確地接收和解析客戶端發送的數據至關重要。.NET WebAPI提供了一系列特性,如[FromRoute]、[FromQuery]和[FromBody],用 ...
  • 原因:我之所以想做這個項目,是因為在之前查找關於C#/WPF相關資料時,我發現講解圖像濾鏡的資源非常稀缺。此外,我註意到許多現有的開源庫主要基於CPU進行圖像渲染。這種方式在處理大量圖像時,會導致CPU的渲染負擔過重。因此,我將在下文中介紹如何通過GPU渲染來有效實現圖像的各種濾鏡效果。 生成的效果 ...
  • 引言 上一章我們介紹了在xUnit單元測試中用xUnit.DependencyInject來使用依賴註入,上一章我們的Sample.Repository倉儲層有一個批量註入的介面沒有做單元測試,今天用這個示例來演示一下如何用Bogus創建模擬數據 ,和 EFCore 的種子數據生成 Bogus 的優 ...
  • 一、前言 在自己的項目中,涉及到實時心率曲線的繪製,項目上的曲線繪製,一般很難找到能直接用的第三方庫,而且有些還是定製化的功能,所以還是自己繪製比較方便。很多人一聽到自己畫就害怕,感覺很難,今天就分享一個完整的實時心率數據繪製心率曲線圖的例子;之前的博客也分享給DrawingVisual繪製曲線的方 ...
  • 如果你在自定義的 Main 方法中直接使用 App 類並啟動應用程式,但發現 App.xaml 中定義的資源沒有被正確載入,那麼問題可能在於如何正確配置 App.xaml 與你的 App 類的交互。 確保 App.xaml 文件中的 x:Class 屬性正確指向你的 App 類。這樣,當你創建 Ap ...
  • 一:背景 1. 講故事 上個月有個朋友在微信上找到我,說他們的軟體在客戶那邊隔幾天就要崩潰一次,一直都沒有找到原因,讓我幫忙看下怎麼回事,確實工控類的軟體環境複雜難搞,朋友手上有一個崩潰的dump,剛好丟給我來分析一下。 二:WinDbg分析 1. 程式為什麼會崩潰 windbg 有一個厲害之處在於 ...
  • 前言 .NET生態中有許多依賴註入容器。在大多數情況下,微軟提供的內置容器在易用性和性能方面都非常優秀。外加ASP.NET Core預設使用內置容器,使用很方便。 但是筆者在使用中一直有一個頭疼的問題:服務工廠無法提供請求的服務類型相關的信息。這在一般情況下並沒有影響,但是內置容器支持註冊開放泛型服 ...
  • 一、前言 在項目開發過程中,DataGrid是經常使用到的一個數據展示控制項,而通常表格的最後一列是作為操作列存在,比如會有編輯、刪除等功能按鈕。但WPF的原始DataGrid中,預設只支持固定左側列,這跟大家習慣性操作列放最後不符,今天就來介紹一種簡單的方式實現固定右側列。(這裡的實現方式參考的大佬 ...