ClickHouse資料副本引擎

大資料技術派發表於2022-12-23

我的gitee地址:https://gitee.com/ddxygq/bigdata-technical-pai ,相關文章都放到這個倉庫裡了。

只有 MergeTree 系列裡的表可支援副本:

  • ReplicatedMergeTree
  • ReplicatedSummingMergeTree
  • ReplicatedReplacingMergeTree
  • ReplicatedAggregatingMergeTree
  • ReplicatedCollapsingMergeTree
  • ReplicatedVersionedCollapsingMergeTree
  • ReplicatedGraphiteMergeTree

副本是表級別的,不是整個伺服器級的。所以,伺服器裡可以同時有複製表和非複製表。

副本不依賴分片。每個分片有它自己的獨立副本。

對於 INSERT 和 ALTER 語句運算元據的會在壓縮的情況下被複制(更多資訊,看 ALTER )。

而 CREATE,DROP,ATTACH,DETACH 和 RENAME 語句只會在單個伺服器上執行,不會被複制。

  • CREATE TABLE 在執行此語句的伺服器上建立一個新的可複製表。如果此表已存在其他伺服器上,則給該表新增新副本。

  • DROP TABLE 刪除執行此查詢的伺服器上的副本。

  • RENAME 重新命名一個副本。換句話說,可複製表不同的副本可以有不同的名稱。

ClickHouse 使用 Apache ZooKeeper 儲存副本的元資訊。請使用 ZooKeeper 3.4.5 或更高版本。

要使用副本,需在 Zookeeper 伺服器的配置部分設定相應引數。

<zookeeper>
    <node index="1">
        <host>example1</host>
        <port>2181</port>
    </node>
    <node index="2">
        <host>example2</host>
        <port>2181</port>
    </node>
    <node index="3">
        <host>example3</host>
        <port>2181</port>
    </node>
</zookeeper>

為了將資料表的後設資料儲存到備用 ZooKeeper 叢集而非預設 ZooKeeper 叢集,我們可以透過如下 SQL 的方式建立使用 ReplicatedMergeTree 引擎的資料表:

CREATE TABLE table_name ( ... ) ENGINE = ReplicatedMergeTree('zookeeper_name_configured_in_auxiliary_zookeepers:path', 'replica_name') ...

你可以配置任何現有的 ZooKeeper 叢集,系統會使用裡面的目錄來存取後設資料(該目錄在建立可複製表時指定)。

如果配置檔案中沒有設定 ZooKeeper ,則無法建立複製表,並且任何現有的複製表都將變為只讀。

SELECT 查詢並不需要藉助 ZooKeeper ,副本並不影響 SELECT 的效能,查詢複製表與非複製錶速度是一樣的。查詢分散式表時,ClickHouse的處理方式可透過設定 max_replica_delay_for_distributed_queries 和 fallback_to_stale_replicas_for_distributed_queries 修改。

對於每個 INSERT 語句,會透過幾個事務將十來個記錄新增到 ZooKeeper。(確切地說,這是針對每個插入的資料塊; 每個 INSERT 語句的每 max_insert_block_size = 1048576 行和最後剩餘的都各算作一個塊。)相比非複製表,寫 zk 會導致 INSERT 的延遲略長一些。但只要你按照建議每秒不超過一個 INSERT 地批次插入資料,不會有任何問題。一個 ZooKeeper 叢集能給整個 ClickHouse 叢集支撐協調每秒幾百個 INSERT。資料插入的吞吐量(每秒的行數)可以跟不用複製的資料一樣高。

對於非常大的叢集,你可以把不同的 ZooKeeper 叢集用於不同的分片。然而,即使 Yandex.Metrica 叢集(大約300臺伺服器)也證明還不需要這麼做。

複製是多主非同步。 INSERT 語句(以及 ALTER )可以發給任意可用的伺服器。資料會先插入到執行該語句的伺服器上,然後被複制到其他伺服器。由於它是非同步的,在其他副本上最近插入的資料會有一些延遲。如果部分副本不可用,則資料在其可用時再寫入。副本可用的情況下,則延遲時長是透過網路傳輸壓縮資料塊所需的時間。為複製表執行後臺任務的執行緒數量,可以透過 background_schedule_pool_size 進行設定。

ReplicatedMergeTree 引擎採用一個獨立的執行緒池進行復制拉取。執行緒池的大小透過 background_fetches_pool_size 進行限定,它可以在重啟伺服器時進行調整。

預設情況下,INSERT 語句僅等待一個副本寫入成功後返回。如果資料只成功寫入一個副本後該副本所在的伺服器不再存在,則儲存的資料會丟失。要啟用資料寫入多個副本才確認返回,使用 insert_quorum 選項。

單個資料塊寫入是原子的。 INSERT 的資料按每塊最多 max_insert_block_size = 1048576 行進行分塊,換句話說,如果 INSERT 插入的行少於 1048576,則該 INSERT 是原子的。

資料塊會去重。對於被多次寫的相同資料塊(大小相同且具有相同順序的相同行的資料塊),該塊僅會寫入一次。這樣設計的原因是萬一在網路故障時客戶端應用程式不知道資料是否成功寫入DB,此時可以簡單地重複 INSERT 。把相同的資料傳送給多個副本 INSERT 並不會有問題。因為這些 INSERT 是完全相同的(會被去重)。去重引數參看伺服器設定 merge_tree 。(注意:Replicated*MergeTree 才會去重,不需要 zookeeper 的不帶 MergeTree 不會去重)

在複製期間,只有要插入的源資料透過網路傳輸。進一步的資料轉換(合併)會在所有副本上以相同的方式進行處理執行。這樣可以最大限度地減少網路使用,這意味著即使副本在不同的資料中心,資料同步也能工作良好。(能在不同資料中心中的同步資料是副本機制的主要目標。)

你可以給資料做任意多的副本。Yandex.Metrica 在生產中使用雙副本。某一些情況下,給每臺伺服器都使用 RAID-5 或 RAID-6 和 RAID-10。是一種相對可靠和方便的解決方案。

系統會監視副本資料同步情況,並能在發生故障後恢復。故障轉移是自動的(對於小的資料差異)或半自動的(當資料差異很大時,這可能意味是有配置錯誤)。

建立複製表

在表引擎名稱上加上 Replicated 字首。例如:ReplicatedMergeTree。

Replicated*MergeTree 引數

  • zoo_path — ZooKeeper 中該表的路徑。

  • replica_name — ZooKeeper 中的該表的副本名稱。

  • other_parameters — 關於引擎的一系列引數,這個引擎即是用來建立複製的引擎,例如,ReplacingMergeTree 。

示例:

CREATE TABLE table_name
(
    EventDate DateTime,
    CounterID UInt32,
    UserID UInt32
) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{layer}-{shard}/table_name', '{replica}')
PARTITION BY toYYYYMM(EventDate)
ORDER BY (CounterID, EventDate, intHash32(UserID))
SAMPLE BY intHash32(UserID)

如上例所示,這些引數可以包含宏替換的佔位符,即大括號的部分。它們會被替換為配置檔案裡 ‘macros’ 那部分配置的值。示例:

<macros>
    <layer>05</layer>
    <shard>02</shard>
    <replica>example05-02-1</replica>
</macros>

ZooKeeper 中該表的路徑對每個可複製表都要是唯一的。不同分片上的表要有不同的路徑。 這種情況下,路徑包含下面這些部分:

/clickhouse/tables/ 是公共字首,我們推薦使用這個。

{layer}-{shard} 是分片標識部分。在此示例中,由於 Yandex.Metrica 叢集使用了兩級分片,所以它是由兩部分組成的。但對於大多數情況來說,你只需保留 {shard} 佔位符即可,它會替換展開為分片標識。

table_name 是該表在 ZooKeeper 中的名稱。使其與 ClickHouse 中的表名相同比較好。 這裡它被明確定義,跟 ClickHouse 表名不一樣,它並不會被 RENAME 語句修改。 HINT:你也可以在 table_name 前面新增一個資料庫名稱。例如: db_name.table_name 。

兩個內建的佔位符 {database} 和 {table} 也可使用,它們可以展開成資料表名稱和資料庫名稱(只有當這些宏指令在 macros 部分已經定義時才可以)。因此 ZooKeeper 路徑可以指定為 '/clickhouse/tables/{layer}-{shard}/{database}/{table}' 。

當使用這些內建佔位符時,請當心資料表重新命名。 ZooKeeper 中的路徑無法變更,而當資料表被重新命名時,宏命令將展開為另一個路徑,資料表將指向一個 ZooKeeper 上並不存在的路徑,並因此轉變為只讀模式。

副本名稱用於標識同一個表分片的不同副本。你可以使用伺服器名稱,如上例所示。同個分片中不同副本的副本名稱要唯一。

你也可以顯式指定這些引數,而不是使用宏替換。對於測試和配置小型叢集這可能會很方便。但是,這種情況下,則不能使用分散式 DDL 語句(ON CLUSTER)。

使用大型叢集時,我們建議使用宏替換,因為它可以降低出錯的可能性。

你可以在伺服器的配置檔案中指定 Replicated 資料表引擎的預設引數。例如:

<default_replica_path>/clickhouse/tables/{shard}/{database}/{table}</default_replica_path>
<default_replica_name>{replica}</default_replica_name>

這樣,你可以在建表時省略引數:

CREATE TABLE table_name (
    x UInt32
) ENGINE = ReplicatedMergeTree
ORDER BY x;

它等價於:

CREATE TABLE table_name (
    x UInt32
) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/{database}/table_name', '{replica}')
ORDER BY x;

在每個副本伺服器上執行 CREATE TABLE 查詢。將建立新的複製表,或給現有表新增新副本。

如果其他副本上已包含了某些資料,在表上新增新副本,則在執行語句後,資料會從其他副本複製到新副本。換句話說,新副本會與其他副本同步。

要刪除副本,使用 DROP TABLE。但它只刪除那個 – 位於執行該語句的伺服器上的副本。

故障恢復

如果伺服器啟動時 ZooKeeper 不可用,則複製表會切換為只讀模式。系統會定期嘗試去連線 ZooKeeper。

如果在 INSERT 期間 ZooKeeper 不可用,或者在與 ZooKeeper 互動時發生錯誤,則丟擲異常。

連線到 ZooKeeper 後,系統會檢查本地檔案系統中的資料集是否與預期的資料集( ZooKeeper 儲存此資訊)一致。如果存在輕微的不一致,系統會透過與副本同步資料來解決。

如果系統檢測到損壞的資料片段(檔案大小錯誤)或無法識別的片段(寫入檔案系統但未記錄在 ZooKeeper 中的部分),則會把它們移動到 ‘detached’ 子目錄(不會刪除)。而副本中其他任何缺少的但正常資料片段都會被複制同步。

注意,ClickHouse 不會執行任何破壞性操作,例如自動刪除大量資料。

當伺服器啟動(或與 ZooKeeper 建立新會話)時,它只檢查所有檔案的數量和大小。 如果檔案大小一致但中間某處已有位元組被修改過,不會立即被檢測到,只有在嘗試讀取 SELECT 查詢的資料時才會檢測到。該查詢會引發校驗和不匹配或壓縮塊大小不一致的異常。這種情況下,資料片段會新增到驗證佇列中,並在必要時從其他副本中複製。

如果本地資料集與預期資料的差異太大,則會觸發安全機制。伺服器在日誌中記錄此內容並拒絕啟動。這種情況很可能是配置錯誤,例如,一個分片上的副本意外配置為別的分片上的副本。然而,此機制的閾值設定得相當低,在正常故障恢復期間可能會出現這種情況。在這種情況下,資料恢復則是半自動模式,透過使用者主動操作觸發。

要觸發啟動恢復,可在 ZooKeeper 中建立節點 /path_to_table/replica_name/flags/force_restore_data,節點值可以是任何內容,或執行命令來恢復所有的可複製表:

sudo -u clickhouse touch /var/lib/clickhouse/flags/force_restore_data

然後重啟伺服器。啟動時,伺服器會刪除這些標誌並開始恢復。

在資料完全丟失後的恢復

如果其中一個伺服器的所有資料和後設資料都消失了,請按照以下步驟進行恢復:

  • 在伺服器上安裝 ClickHouse。在包含分片識別符號和副本的配置檔案中正確定義宏配置,如果有用到的話,
  • 如果伺服器上有非複製表則必須手動複製,可以從副本伺服器上(在 /var/lib/clickhouse/data/db_name/table_name/ 目錄中)複製它們的資料。
  • 從副本伺服器上中複製位於 /var/lib/clickhouse/metadata/ 中的表定義資訊。如果在表定義資訊中顯式指定了分片或副本識別符號,請更正它以使其對應於該副本。(另外,啟動伺服器,然後會在 /var/lib/clickhouse/metadata/ 中的.sql檔案中生成所有的 ATTACH TABLE 語句。)
  • 要開始恢復,ZooKeeper 中建立節點 /path_to_table/replica_name/flags/force_restore_data,節點內容不限,或執行命令來恢復所有複製的表:sudo -u clickhouse touch /var/lib/clickhouse/flags/force_restore_data

然後啟動伺服器(如果它已執行則重啟)。資料會從副本中下載。

另一種恢復方式是從 ZooKeeper(/path_to_table/replica_name)中刪除有資料丟的副本的所有元資訊,然後再按照«建立可複製表»中的描述重新建立副本。

恢復期間的網路頻寬沒有限制。特別注意這一點,尤其是要一次恢復很多副本。

MergeTree 轉換為 ReplicatedMergeTree

我們使用 MergeTree 來表示 MergeTree系列 中的所有表引擎,ReplicatedMergeTree 同理。

如果你有一個手動同步的 MergeTree 表,您可以將其轉換為可複製表。如果你已經在 MergeTree 表中收集了大量資料,並且現在要啟用複製,則可以執行這些操作。

如果各個副本上的資料不一致,則首先對其進行同步,或者除保留的一個副本外,刪除其他所有副本上的資料。

重新命名現有的 MergeTree 表,然後使用舊名稱建立 ReplicatedMergeTree 表。 將資料從舊錶移動到新表(/var/lib/clickhouse/data/db_name/table_name/)目錄內的 ‘detached’ 目錄中。 然後在其中一個副本上執行ALTER TABLE ATTACH PARTITION,將這些資料片段新增到工作集中。

ReplicatedMergeTree 轉換為 MergeTree

使用其他名稱建立 MergeTree 表。將具有ReplicatedMergeTree表資料的目錄中的所有資料移動到新表的資料目錄中。然後刪除ReplicatedMergeTree表並重新啟動伺服器。

如果你想在不啟動伺服器的情況下清除 ReplicatedMergeTree 表:

  • 刪除後設資料目錄中的相應 .sql 檔案(/var/lib/clickhouse/metadata/)。

  • 刪除 ZooKeeper 中的相應路徑(/path_to_table/replica_name)。

之後,你可以啟動伺服器,建立一個 MergeTree 表,將資料移動到其目錄,然後重新啟動伺服器。

相關文章