Clickhouse - MergeTree原理
MergeTree
引擎以及隸屬於MergeTree
引擎族的所有引擎是Clickhouse表引擎中最重要, 最強大的引擎.
MergeTree引擎族中的引擎被設計用於將大量數據寫入表中. 這些數據被快速的寫入每個表的每個part, 然后在Clickhouse底層會進行多個parts的合並(merge). 這種形式的處理比在插入過程中不斷重寫存儲中的數據要高效得多.
主要的功能點:
-
存儲按主鍵(primary key)排序的數據.
這允許用戶可以創建一個小型的稀疏索引, 有利於更快的在表中找到索要的數據.
-
如果
partitioning key
被設置, 分片(partitions)可以被使用.Clickhouse支持某些帶分區的操作, 對於同一份數據進行處理, 帶有分區的操作會比一般操作更有效. 當在查詢語句中指定了分區后, Clickhouse會根據分區信息來進行數據的切分, 這樣極大程度上提升了查詢的性能.
-
數據副本機制支持(Replication)
ReplicatedMergeTree並引擎提供了數據副本機制.
-
支持數據采樣
如果必要, 可以在表中設置數據采樣方式.
1. 創建表
1.1. 建表語句
CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
(
name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1] [TTL expr1],
name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2] [TTL expr2],
...
INDEX index_name1 expr1 TYPE type1(...) GRANULARITY value1,
INDEX index_name2 expr2 TYPE type2(...) GRANULARITY value2
) ENGINE = MergeTree()
ORDER BY expr
[PARTITION BY expr]
[PRIMARY KEY expr]
[SAMPLE BY expr]
[TTL expr [DELETE|TO DISK 'xxx'|TO VOLUME 'xxx'], ...]
[SETTINGS name=value, ...]
1.2. 參數說明
-
ENGINE
: 指定該表所使用的的引擎. 如果引擎需要一些具體的參數, 需要進行相應的設置,MergeTree
引擎不需要, 例如ReplicatedMergeTree
:ENGINE=ReplicatedMergeTree('/clickhouse/tables/#_tenant_id_#/#__appname__#/#_at_date_#/{shard}/hits', '{replica}')
-
ORDER BY
: 按指定字段進行排序支持設置的值為元組(一個列名或任意多字段表達式), 例如:
ORDER BY (CounterID, EventDate)
如果創建表的時候, 沒有使用
PRIMARY KEY
修飾詞來顯式指定主鍵, 那么Clickhouse會將ORDER BY
指定的字段作為主鍵處理.如果該表不需要排序, 可以使用
ORDER BY tuple()
語法. -
PARTITION BY
: 指定分區字段, 可選分區字段一般是一個
Date
類型或者DateTime
類型的字段. 如果想按照月進行分區, 可以使用toYYYYMM(date_column)
表達式, 那么分區字段的格式就是YYYYMM
. -
PAIMARY KEY
: 主鍵, 可選默認情況下, 在Clickhouse中主鍵都是和排序字段(
ORDER BY
子句指定字段)是一致的, 所以在大多數情況下, 不需要單獨指定主鍵. 如果需要兩者不一致, 詳情可見differs from the sorting key. -
SAMPLE BY
: 取樣表達式, 可選如果使用了
SAMPLE BY
子句, 那么SAMPLE BY
指定的字段必須是主鍵(或排序字段)所包含的字段. 例如:SAMPLE BY intHash32(USERID) ORDER BY (CounterID, EventDate, intHash32(UserID))
-
TTL
: 一些被指定的規則, 涉及存儲數據行的存儲時長, 以及確定數據parts的自動移除邏輯. 可選TTL表達式必須包含一個
Date
類型或者DateTime
類型的字段作為一個結果值, 例如:TTL date + INTERVAL 1 DAY
這種類型的規則
DELETE|TO DISK 'xxx'|TO VOLUME 'xxx'
指定了當表達式條件被滿足時對數據parts處理的操作: 刪除過期的行, 移除數據parts(如果該數據parts中所有的行都滿足條件)到指定的位置TO DISK 'XXX'
.規則的默認類型是DELETE
. 可以指定多個規則組成一條表達式, 但是DELETE
規則不成超過一條.查看更多細節: TTL for columns and tables.
-
SETTINGS
: 設置MergeTree
引擎的附加的參數, 可選-
index_graularity
索引標記之間的最大數據行數. 默認值: 8192.
-
index_graularity_bytes
數據顆粒的最大大小, 單位bytes, 默認值: 10MB. 若要僅按行數限制顆粒大小, 請設置為0(不推薦).
-
min_index_granularity_bytes
數據顆粒的最小大小, 單位bytes, 默認值: 1024b. 該值是為了防止意外創建
index_graularity_bytes
值很低的表. -
enable_mixed_granularity_parts
同樣是設置數據顆粒的最大大小, 該參數用於過渡取代
index_graularity_bytes
. 在v19.11
版本前, 只有index_granularity
用於控制顆粒大小.當查詢數據涉及到大量的行(幾十或幾百兆)時,index_granularity_bytes
參數提高了Clickhouse的性能. 如果你的表擁有大量的行時, 可以為表啟用這個參數來提高查詢的性能. -
use_minimalistic_part_header_in_zookeeper
在Zookeeper中數據parts_headers存儲的方式. 如果設置為1, 則Zookeeper中存儲的數據較少, 具體可以參考setting description.
-
min_merge_bytes_to_use_direct_io
使用直接I/O訪問存儲磁盤所需的merge操作的最小數據量. merge數據parts時, Clickhouse會計算所有要合並的數據的總存儲空間, 如果超過
min_merge_bytes_to_use_direct_io
設定的值, Clickhouse會使用直接I/O接口(O_DIRECT選項)讀取和寫入數據到磁盤. 如果設置為0, 則直接I/O被禁用, 默認值: 10M. -
merge_with_ttl_timeout
重復合並TTL的最小延遲時間, 單位秒, 默認值: 1 day.
-
write_final_mark
啟用或禁用在數據部分的末尾(最后一個字節之后)寫入最后的索引標記. 默認值: 1, 不要關閉.
-
merge_max_block_size
合並操作時, 每個塊的最大行數, 默認值: 8192.
-
storage_policy
-
min_bytes_for_wide_part
,min_rows_for_wide_part
數據部分中可存儲為Wide格式的最小字節數/行數. 可以設置這些設置中的一個, 兩個或全部.
-
1.3. 樣例說明
ENGINE MergeTree()
PARTITION BY toYYYYMM(EventDate)
ORDER BY (CounterID, EventDate, intHash32(UserID))
SAMPLE BY intHash32(UserID)
SETTINGS index_granularity=8192
在這個樣例中, 設置了按月份進行分區.
同時設置了一個表達式 -> 按用戶ID進行Hash采樣. 這樣就可以為每個CounterID和EventDate偽隨機化(pseudorandomize)表中的數據. 如果你在選擇數據時定義了一個SAMPLE子句, Clickhouse將會為用戶的子集返回一個均勻的偽隨機化的數據樣本.
index_granularity設置可以省略, 因為8192是默認值.
2. 數據存儲
一個表由按主鍵排序的數據parts組成.
當在表中插入數據時, 會創建單獨的數據parts, 並對每個數據parts按主鍵進行字典排序. 例如, 如果主鍵是(CounterID, Date), 則該部分的數據會按照CounterID排序, 在每個CounterID內, 再按照Date排序.
屬於不同分片的數據會被分隔成不同的part. 在Clickhouse底層, ck合並數據parts成為更有效的存儲結構. 屬於不同分片的parts不會被merge. Merge機制並不能保證所有具有相同主鍵的行都在同一個數據parts.
數據parts可以用寬(Wide)或者緊湊(Compact)格式來存儲. 在Wide格式中, 每一列都會被存儲在磁盤的一個單獨的文件中, 在Compact格式中, 所有的列都會被存在在同一個文件中. Compact格式可以用來提高小規模和頻繁插入的性能(涉及到寫入到更少的文件中).
數據存儲格式有表引擎的min_bytes_for_wide_part
和min_rows_for_wide_part
參數控制. 如果數據parts的字節數小於相應的設置項, 則該部分以Compact格式存儲, 反之以Wide格式存儲. 如果這兩個參數未被設置, 將會默認以Wide格式存儲.
每個數據part在邏輯上被划分成顆粒(granule). 一個顆粒是Clickhouse在讀取選中數據時, 最小的不可分割的數據集單位. Clickhouse不會拆分行或者字段值, 所以每個顆粒總是包含整數單位的行. 顆粒的第一行是用該行的主鍵值進行標記的. 對於每個數據part來說, Clickhouse都會創建一個索引文件來存儲這些標記(mark). 對於每一列而言, 無論它是否是主鍵, Clickhouse都會存儲相同的標記. 這些標記可以幫助查詢的時候直接在列文件中找到數據.
顆粒的大小收到表引擎index_granularity
和index_granularity_bytes
參數的限制. 根據行的大小, 一個顆粒中的行數在[1, index_granularity]范圍內. 如果單行的大小大於設置值, 那么顆粒的大小可以超過index_granularity_bytes. 在這種情況下, 顆粒的大小等於行的大小.