Clickhouse表引擎探究-ReplacingMergeTree

来源:https://www.cnblogs.com/Jcloud/archive/2022/12/21/16994044.html
-Advertisement-
Play Games

作者:耿巨集宇 1 表引擎簡述 1.1 官方描述 MergeTree 系列的引擎被設計用於插入極大量的數據到一張表當中。數據可以以數據片段的形式一個接著一個的快速寫入,數據片段在後臺按照一定的規則進行合併。相比在插入時不斷修改(重寫)已存儲的數據,這種策略會高效很多。 ReplacingMergeTr ...


作者:耿巨集宇

1 表引擎簡述

1.1 官方描述

MergeTree 系列的引擎被設計用於插入極大量的數據到一張表當中。數據可以以數據片段的形式一個接著一個的快速寫入,數據片段在後臺按照一定的規則進行合併。相比在插入時不斷修改(重寫)已存儲的數據,這種策略會高效很多。

ReplacingMergeTree 引擎和 MergeTree 的不同之處在於它會刪除排序鍵值相同的重覆項。
數據的去重只會在數據合併期間進行。合併會在後臺一個不確定的時間進行,因此你無法預先作出計劃。有一些數據可能仍未被處理。儘管你可以調用 OPTIMIZE 語句發起計劃外的合併,但請不要依靠它,因為 OPTIMIZE 語句會引發對數據的大量讀寫。

1.2 本地表語法

CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
(
name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1],
name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2],
...
) ENGINE = ReplacingMergeTree([ver])
[PARTITION BY expr]
[PRIMARY KEY expr]
[ORDER BY expr]
[SAMPLE BY expr]
[TTL expr [DELETE|TO DISK 'xxx'|TO VOLUME 'xxx'], ...]
[SETTINGS name=value, ...]

參數介紹

  • ver — 版本列。類型為 UInt*, Date 或 DateTime。可選參數。
    在數據合併的時候,ReplacingMergeTree 從所有具有相同排序鍵的行中選擇一行留下:
    1.如果 ver 列未指定,保留最後一條。
    2.如果 ver 列已指定,保留 ver 值最大的版本。

  • PRIMARY KEY expr 主鍵。如果要 選擇與排序鍵不同的主鍵,在這裡指定,可選項。
    預設情況下主鍵跟排序鍵(由 ORDER BY 子句指定)相同。 因此,大部分情況下不需要再專門指定一個 PRIMARY KEY 子句。

  • SAMPLE BY EXPR 用於抽樣的表達式,可選項

  • PARTITION BY expr 分區鍵

  • ORDER BY expr 排序鍵

    1.3 分區表語法

CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
(
name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1],
name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2],
...
) ENGINE = Distributed(cluster, database, table[, sharding_key[, policy_name]])
[SETTINGS name=value, ...]

參數介紹

  • cluster 集群名
  • table 遠程數據表名
  • sharding_key 分片規則
  • policy_name 規則名,它會被用作存儲臨時文件以便非同步發送數據

2 鍵的概念

Clickhouse的部署,分為單機模式和集群模式,還可以開啟副本。兩種模式,數據表在創建語法、創建步驟和後續的使用方式上,存在一定的差異。

在定義表結構時,需要指定不同的鍵,作用如下。

分片:所有分片節點的權重加和得到S,可以理解為sharing動作取模的依據,權重X=W/S。分片鍵 Mod S 得到的值,與哪個分片節點匹配,則會寫入哪個分片。不同分片可能存在於不同的集群節點,即便不同分片在同一節點,但ck在merge時,維度是同一分區+同一分片,這是物理文件的合併範圍。
如果我們權重分別設置為1,2,3 那麼總權重是6,那麼總區間就是[0,6),排在shard配置第一位的node01,權重占比為1/6,所以屬於區間[0,1),排在shard配置第二位的node02,占比2/6,所以區間為[1,3),至於最後的node03就是[3,6).所以如果rand()產生的數字除以6取餘落在哪個區間,數據就會分發到哪個shard,通過權重配置,可以實現數據按照想要的比重分配.

3 分片的作用

3.1 分片規則

在分散式模式下,ClickHouse會將數據分為多個分片,並且分佈到不同節點上。不同的分片策略在應對不同的SQL Pattern時,各有優勢。ClickHouse提供了豐富的- - - sharding策略,讓業務可以根據實際需求選用。

  • random隨機分片:寫入數據會被隨機分發到分散式集群中的某個節點上。
  • constant固定分片:寫入數據會被分發到固定一個節點上。
  • column value分片:按照某一列的值進行hash分片。
  • 自定義表達式分片:指定任意合法表達式,根據表達式被計算後的值進行hash分片。

3.2 類比

以MySQL的分庫分表場景為例:

  • 2個庫,1個表分4個子表,採用一主一從模式。
  • db01包含tab-1和tab-2,db-2包含tab-3和tab-4;
  • 在配置sharding規則時,需要設置分庫規則、分表規則;
    一條記錄寫入時,會計算它要寫入哪個表、哪個庫,寫入的記錄會被從節點複製。

這個MySQL的例子,與CK的分區+分片+副本在邏輯上基本一致。分區理解為數據寫入哪個表,分片可以理解為數據寫入哪個庫,副本則是從節點的拷貝。

3.3 分片、分區與副本

Clickhouse分片是集群模式下的概念,可以類比MySQL的Sharding邏輯,副本是為瞭解決Sharing方案下的高可用場景所存在的。
下圖描述了一張Merge表的各類鍵的關係,也能反映出一條記錄的寫入過程。

4 數據合併限制

理清了分區與分片的概念,也就明白CK的數據合併,為什麼要限制相同分區、相同分片,因為它們影響數據的存儲位置,merge操作只能針對相同物理位置(分區目錄)的數據進行操作,而分片會影響數據存儲在哪個節點上。
一句話,使用CK的ReplacingMergeTree引擎的去重特性,期望去重的數據,必須滿足擁有 相同排序鍵、同一分區、同一分片。
接下來針對這一要求,在數據上進行驗證。

5 數據驗證

5.1 場景設置

這裡是要驗證上面的結論,“期望去重的數據,必須滿足在相同排序鍵、同一分區、同一分片”;
首先擁有相同排序鍵才會在merge操作時進行判斷為重覆,因此保證測試數據的排序鍵相同;剩餘待測試場景則是分區與分片。
由此進行場景設置:

  • 相同記錄,能夠寫入同一分區、同一分片
  • 相同記錄,能夠寫入同一分區,不同分片
  • 相同記錄,能夠寫入不同分區,不同分片
  • 相同記錄,能夠寫入不同分區、相同分片
    再疊加同步寫入方式:
  • 直接寫本地表
  • 直接寫分散式表
    補充:分區鍵與分片鍵,是否必須相同?

5.2 第一天測試

場景1: 相同記錄,能夠寫入同一分區、同一分片

一次執行3條插入,插入本地表
[main_id=101,sku_id=SKU0002;barnd_code=BC01,BC02,BC03]
select * from test_ps.sku_detail_same_partition_same_shard_all;

分三次執行,插入本地表
[main_id=101,sku_id=SKU0001;barnd_code=BC01,BC02,BC03]
select * from test_ps.sku_detail_same_partition_same_shard_all;

分三次執行,插入分散式表
[main_id=101,sku_id=SKU0001;barnd_code=BC001,BC002,BC003]
select * from test_ps.sku_detail_same_partition_same_shard_all;

select * from test_ps.sku_detail_same_partition_same_shard_all final;

結論1
1.採用分散式表插入數據,保證分片鍵、分區鍵的值相同,才能保證merge去重成功
排除本地表插入場景
2.採用本地表插入數據,在分片鍵、分區鍵相同的情況下,無法保證merge去重

  • 在一個session(一次提交)裡面包含多個記錄,直接會得到一條記錄,插入過程去重
    在第一次insert時,準備的3條insert語句是一次執行的,查詢後只有1條記錄。
  • 在多個session(多次提交)記錄,不會直接去重,但有可能寫到不同集群節點,導致無法去重
    分3次執行3條insert語句,查詢後有3條記錄,且通過final查詢後有2條記錄,合併去重的那2條記錄是寫入在同一集群節點。【參考SKU0002的執行結果】

後面直接驗證插入分散式表場景。

場景2:相同記錄,能夠寫入同一分區,不同分片

  • 分片鍵採用的rand()方式,隨機生成。

分三次執行,插入分散式表
[main_id=103,sku_id=SKU0003;barnd_code=BC301,BC302,BC303]
檢查數據插入狀態
select * from test_ps.sku_detail_same_partition_diff_shard_all where main_id =103 ;

檢查merge的去重結果
select * from test_ps.sku_detail_same_partition_diff_shard_all final where main_id =103 ;

分五次執行,插入分散式表
[main_id=104,sku_id=SKU0004;barnd_code=BC401,BC402,BC403,BC404,BC405]
檢查數據插入狀態
select * from test_ps.sku_detail_same_partition_diff_shard_all where main_id =104 ;

檢查merge的去重結果
select * from test_ps.sku_detail_same_partition_diff_shard_all final where main_id =104 ;

結論2

採用分散式表插入數據,保證分區鍵的值相同、分片鍵的值隨機,無法保證merge去重

  • 如果插入記錄時,通過rand()生成的數字取模後的值一樣,很幸運最終可以merge去重成功
  • 如果插入記錄時,通過rand()生成的數字取模後的值不一樣,最終無法通過merge去重

場景3:相同記錄,能夠寫入不同分區,不同分片

  • 分片鍵採用的rand()方式,隨機生成;
  • 分區鍵為了方便測試,採用創建時間。

分五次執行,插入分散式表
[main_id=105,sku_id=SKU0005;barnd_code=BC501,BC502,BC503,BC504,BC505]

檢查數據插入狀態
select * from test_ps.sku_detail_diff_partition_diff_shard_all where main_id =105 ;

檢查merge的去重結果
select * from test_ps.sku_detail_diff_partition_diff_shard_all final where main_id =105;

結論3
採用分散式表插入數據,分區鍵的值與排序鍵不一致、分片鍵的值隨機,無法保證merge去重

  • 按當前測試結果,雖然create_time都不相同,也就是分區不同,也發生了數據合併
  • 數據發生合併,但結果並不是完全按排序鍵進行合併的

場景4:相同記錄,能夠寫入 不同分區、相同分片

  • 分片鍵採用main_id;
  • 分區鍵為了方便測試,採用創建時間。

分六次執行,插入分散式表
[main_id=106,sku_id=SKU0006;barnd_code=BC601,BC602,BC603,BC604,BC605,BC606]

檢查數據插入狀態
select * from test_ps.sku_detail_diff_partition_same_shard_all where main_id =106 ;

檢查merge的去重結果
select * from test_ps.sku_detail_diff_partition_same_shard_all final where main_id =106;

此場景,經過第二天檢索,數據並沒有進行merge,而是用final關鍵字依然能檢索出去重後的結果。也就是說final關鍵字只是在記憶體中進行去重,由於所在分區不同,文件是沒有進行merge合併的,也就沒有去重。反觀相同分區、相同分片的數據表,數據已經完成了merge合併,普通檢索只能得到一條記錄。

結論4
採用分散式表插入數據,分區鍵的值與排序鍵不一致、分片鍵的值固定,無法實現merge去重

5.3 第二天檢查

以下均採用普通查詢,發現如下情況

  • 分片不同的表,其數據沒有合併
  • 分片相同、分區不同的沒有合併
  • 分片相同、分區相同的已經完成了合併

select * from test_ps.sku_detail_same_partition_same_shard_all;

select * from test_ps.sku_detail_same_partition_diff_shard_all;

select * from test_ps.sku_detail_diff_partition_diff_shard_all;

select * from test_ps.sku_detail_diff_partition_same_shard_all;

6 總結

根據測試結果,在不同場景下的合併情況:

  • 如果數據存在在相同分片,且相同分區,絕對可以實現合併去重。
  • 如果數據存儲在不同分片,不同分區,將不會進行合併去重。
  • 如果數據存儲在不同分片,但同一分片內保證在相同分區,會進行此分片下的merge去重。
  • 如果數據存在在相同分片,但不同分區,不會進行merge去重,但通過final關鍵字可以在CK記憶體中對相同分區、相同分片的數據進行去重。

在Clickhouse的ReplacingMergeTree進行merge操作時,是根據排序鍵(order by)來識別是否重覆、是否需要合併。而分區和分片,影響的是數據的存儲位置,在哪個集群節點、在哪個文件目錄。那麼最終ReplacingMergeTree表引擎在合併時,只會在當前節點、且物理位置在同一表目錄下的數據進行merge操作。

最後,我們在設計表時,如果期望利用到ReplacingMergeTree自動去重的特性,那麼必須使其存儲在相同分區、相同分片下; 而在設置分區鍵、分片鍵時,二者不要求必須相同,但必須穩定,穩定的含義是入參相同出參必須相同。


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

-Advertisement-
Play Games
更多相關文章
  • 我國目前並未出台專門針對網路爬蟲技術的法律規範,但在司法實踐中,相關判決已屢見不鮮,K 哥特設了“K哥爬蟲普法”專欄,本欄目通過對真實案例的分析,旨在提高廣大爬蟲工程師的法律意識,知曉如何合法合規利用爬蟲技術,警鐘長鳴,做一個守法、護法、有原則的技術人員。 案情介紹 四川省德昌縣人民檢察院起訴書指控 ...
  • JZ48 最長不含重覆字元的子字元串 描述 請從字元串中找出一個最長的不包含重覆字元的子字元串,計算該最長子字元串的長度。 示例1 輸入:"abcabcbb" 返回值:3 說明:因為無重覆字元的最長子串是"abc",所以其長度為 3。 方法1 思路 維護一個數組,想裡面添加元素,直至出現第一個重覆元 ...
  • 摘要:Fork/Join框架位於J.U.C(java.util.concurrent)中,是Java7中提供的用於執行並行任務的框架,其可以將大任務分割成若幹個小任務,最終彙總每個小任務的結果後得到最終結果。 本文分享自華為雲社區《如何使用Java7提供的Fork/Join框架實現高併發程式?》,作 ...
  • WPF以其豐富靈活的控制項樣式設計,相較於WinForm而言,一直是工控組態軟體的寵兒。經過前兩文章的學習,已經對WPF開發工控組態軟體有了一個基本的瞭解, 今天繼續學習溫度計的開發,僅供學習分享使用,如有不足之處,還請指正。 ...
  • .NetCore配置使用 什麼是JWT(詳情在面試.md中有) JWT是目前最流行的跨域身份驗證解決方案,JWT的原則是在伺服器身份驗證之後,會返回一個Json數據,,之後,當用戶與伺服器通信的時候,客戶在請求中發揮Json對象,伺服器會根據這個json對象來判斷用戶的身份,.為了防止用戶篡改信息, ...
  • 支持.Net/.Net Core/.Net Framework,可以部署在Docker, Windows, Linux, Mac。 由於該工具近來被廣東省數個公司2B項目採用,且表現穩定,得到良好驗證,故在此推薦出來。 此工具在IDataAccess介面中提供。 IDataAccess所在的命名空間 ...
  • 說明 基於微服務項目,產生的的多項目倉庫管理腳本。可直接保存 shell 腳本後酌情修改後試用 目錄結構 xxxx Xxx1Api/ Xxx2Api/ git_clone_api.sh git_branch_dev.sh git_pull_all.sh git_status.sh api-build ...
  • 摘要:如流水般源源不斷的數據都存放在哪裡?雲原生資料庫到底是什麼?企業基於雲原生資料庫如何隨取隨用,實現從 “上好雲” 到 “用好雲” 的跨越發展? 本文分享自華為雲社區《探秘・雲新知丨華為雲解鎖雲原生資料庫發展新動能》,作者:華為雲頭條 。 伴隨數字化轉型步入 “深水區”,企業的業務需求迭代迅速, ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...