Clickhouse - MergeTree原理


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

      存儲策略, 詳見Using Multiple Block Devices for Data Storage.

    • 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_partmin_rows_for_wide_part參數控制. 如果數據parts的字節數小於相應的設置項, 則該部分以Compact格式存儲, 反之以Wide格式存儲. 如果這兩個參數未被設置, 將會默認以Wide格式存儲.

每個數據part在邏輯上被划分成顆粒(granule). 一個顆粒是Clickhouse在讀取選中數據時, 最小的不可分割的數據集單位. Clickhouse不會拆分行或者字段值, 所以每個顆粒總是包含整數單位的行. 顆粒的第一行是用該行的主鍵值進行標記的. 對於每個數據part來說, Clickhouse都會創建一個索引文件來存儲這些標記(mark). 對於每一列而言, 無論它是否是主鍵, Clickhouse都會存儲相同的標記. 這些標記可以幫助查詢的時候直接在列文件中找到數據.

顆粒的大小收到表引擎index_granularityindex_granularity_bytes參數的限制. 根據行的大小, 一個顆粒中的行數在[1, index_granularity]范圍內. 如果單行的大小大於設置值, 那么顆粒的大小可以超過index_granularity_bytes. 在這種情況下, 顆粒的大小等於行的大小.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM