第 4 章データ定義#
データパーティションの基本操作#
パーティション情報のクエリ#
ClickHouse には、自己の状態情報をクエリするための多くの system システムテーブルが組み込まれています。その中で、parts システムテーブルはデータテーブルのパーティション情報をクエリするために特化しています。
select partition_id, name, table, database from system.parts where table = 'partition_v2'
指定したパーティションの削除#
合理的なパーティションキーの設計とパーティションの削除機能を利用することで、データの更新を達成できます。
alter table tb_anme drop partition partition_expr
パーティションデータのコピー#
ClickHouse は、A テーブルのパーティションデータを B テーブルにコピーすることをサポートしています。この機能は、迅速なデータ書き込みや複数テーブル間のデータ同期、バックアップなどのシナリオで使用できます。
alter table B replace partition partition_expr from A
注意:以下の 2 つの前提条件を満たす必要があります:
- 2 つのテーブルは同じパーティションキーを持つ必要があります
- それらのテーブル構造は完全に同じである必要があります
パーティションデータのリセット#
データテーブルの特定の列のデータに誤りがある場合、初期値にリセットする必要があります。この場合、以下の文を使用して実現できます:
alter table tb_name clear column column_name in partition partition_expr
準則:デフォルト値の式が宣言されている場合は、その式が優先されます。そうでない場合は、対応するデータ型のデフォルト値が優先されます。
パーティションのアンロードとロード#
テーブルパーティションは、DETACH 文を使用してアンロードできます。パーティションがアンロードされると、物理データは削除されず、テーブルディレクトリの detached サブディレクトリに移動されます。パーティションをロード(ATTACH)することは、その逆の操作です。これは、パーティションデータの移行やバックアップシナリオでよく使用されます。
alter table tb_name detach partition partition_expr
分散 DDL の実行#
ClickHouse はクラスター モードをサポートしており、1 つ以上のノードを持つクラスターがあります。CREATE、ALTER、DROP、RENAME、および TRUNCATE などの DDL 文は、分散実行をサポートしています。クラスター内の任意のノードで DDL 文を実行すると、クラスター内のすべてのノードが同じ順序で同じ文を実行します。ON CLUSTER cluster_name 宣言を追加するだけです。
データの書き込み#
INSERT 文は 3 つの構文スタイルをサポートしています。
最初のスタイル:values 構文
INSERT INTO [db.]table [(c1,c2,c3...)] VALUES (v11,v12,c13),(v21,v22,v23),...
2 番目のスタイル:指定フォーマットの構文
INSERT INTO [db.]table [(c1,c2,c3...)] FORMAT format_anme data_set
3 番目のスタイル:select 句形式の構文を使用
INSERT INTO [db.]table [(c1,c2,c3...)] SELECT ...
VALUES および SELECT 句の形式は、式または関数をサポートしますが、式および関数は追加のパフォーマンスオーバーヘッドをもたらします。
データの削除と変更#
ClickHouse は DELETE および UPDATE の機能を提供しており、これらの操作は Mutation クエリと呼ばれ、ALTER 文の変種と見なすことができます。
違い:Mutation 文は「非常に重い」操作であり、大量データの変更や削除に適しています。次に、トランザクションをサポートしておらず、文が提出されて実行されると、既存のデータに即座に影響を与え、ロールバックできません。最後に、この文の実行は非同期のバックグラウンドプロセスであり、文が提出されるとすぐに戻ります。したがって、具体的なロジックが実行されたことを示すものではなく、その具体的な実行進捗は system.mutations システムテーブルを通じてクエリする必要があります。
SELECT database, table, mutation_id, block_numbers as num, is_done FROM system.mutations
第 5 章データ辞書#
データ辞書は、キーと値および属性のマッピングの形式でデータを定義します。データは ClickHouse の起動時に自動的にロードされるか、最初のクエリ時に遅延的にロードされ(パラメータ設定によって決まります)、動的に更新をサポートします。これは、オープンまたは頻繁に使用されるディメンションテーブルデータを保存するのに非常に適しており、不必要な JOIN クエリを回避できます。
データ辞書には、組み込み(デフォルトで付属する辞書)と拡張(カスタム設定によって実現される)があります。
組み込み辞書#
第 6 章 MergeTree 原理解析#
MergeTree の作成方法とストレージ構造#
MergeTree の作成方法#
CREATE TABLE [IF NOT EXISTS] [db_name.]table_name (
name1 [type]
...
) ENGINE = MergeTree()
[PARTITION BY expr]
[ORDER BY expr]
[PRIMARY KEY expr]
[SAMPLE BY expr]
[SETTINGS name=value, ...]
(1) PARTITION BY [オプション]: パーティションキーで、テーブルデータをさまざまな基準でパーティション分けするために指定します。単一の列フィールドであることも、タプル形式の複数の列フィールドであることも、リスト式であることもできます。宣言しない場合は all と命名されます。データパーティションを合理的に使用することで、クエリ時のデータファイルのスキャン範囲を効果的に減少させることができます
(2) ORDER BY [必須]:ソートキーで、データがさまざまな基準でソートされるように、データスニペット内で指定します。デフォルトは主キーです。単一の列でも、タプル形式の複数の列でも可能です。
(3) PRIMARY KEY [オプション]: 主キーで、主キー列に基づいて一次インデックスを生成し、テーブルクエリを加速します。デフォルトでは、主キーとソートキーは同じです。MergeTree の主キーは重複データを許可します。
(4) SAMPLE BY [オプション]: サンプリング式で、データのサンプリング基準を宣言します。主キーの設定でも同じ式を宣言する必要があります。
(5) SETTINGS: index_granularity [オプション] は、インデックスの粒度を示し、デフォルトは 8192 です。8192 行ごとにインデックスが生成されます。
(6) SETTINGS: index_granularity_bytes [オプション]、19.12 バージョン以降にサポートされます。自動的な間隔サイズの特性を追加しました(各バッチの書き込みデータのサイズに基づいて動的に間隔サイズを分割)、デフォルトは 10M、0 に設定すると自動機能が無効になります。
(7) SETTINGS: enable_mixed_granularity_parts [オプション]:インデックス間隔の自動調整機能を設定し、デフォルトで有効です。
(8) SETTINGS: merge_with_ttl_timeout [オプション]: 19.6 以降に提供されるデータ TTL 機能
(9) SETTINGS: storage_policy [オプション]:マルチパスストレージポリシー
データパーティション#
データのパーティションルール#
MergeTree データパーティションのルールは、パーティション ID によって決まります。パーティション ID の生成ロジックには 4 つのルールがあります:
(1) パーティションキーを指定しない:パーティションの ID はデフォルトで all と命名され、すべてのデータが all パーティションに書き込まれます。
(2) 整数を使用:パーティションキーの値が整数型で、日付型 YYYMMDD 形式に変換できない場合、その整数の文字形式が出力され、パーティション ID の値として使用されます。
(3) 日付型を使用:パーティションキーの値が日付型であるか、または YYYMMDD 形式に変換できる整数である場合、YYYYMMDD 形式でフォーマットされた文字形式が出力され、パーティション ID の値として使用されます。
(4) その他の型を使用:パーティションキーの値が整数型でも日付型でもない場合、128 ビットのハッシュアルゴリズムを使用して ID を生成します。
パーティションディレクトリの命名規則#
完全なパーティションディレクトリの命名公式は以下の通りです。
PartitionID_MinBlockNum_MaxBlockNum_Level
(1) PartitionID:パーティション ID
(2) MinBlockNum_MaxBlockNum: 最小データブロックと最大データブロック番号。ここでの BlockNum は整数の自動増加番号です。
(3) Level:マージのレベルで、特定のパーティションがマージされた回数、またはそのパーティションの年齢と理解できます。
パーティションディレクトリのマージプロセス#
MergeTree のパーティションディレクトリは、データテーブルが作成された後に存在するのではなく、データが書き込まれる過程で作成されます。彼のパーティションディレクトリは、設立された後も一成不変ではありません。
新しいディレクトリ名のマージ方法は以下のルールに従います:
- MinBlockNum:同じパーティション内のすべてのディレクトリの最小値を取ります
- MaxBlockNum: 同じパーティション内のすべてのディレクトリの最大値を取ります
- Level:同じパーティション内の最大 Level 値を取って 1 を加えます
一次インデックス#
MergeTree は index_granularity 間隔に基づいて、データテーブルに一次インデックスを生成し、primary.idx ファイルに保存します。インデックスデータは PRIMARY_KEY 順に並べられます。
疎インデックス#
密なインデックスでは、各行のインデックスマークが具体的なデータレコードに対応しますが、疎インデックスでは、各行のインデックスマークがデータの一部に対応します。
疎インデックスの利点は、少数のインデックスマークを使用して大量のデータの区間位置情報を記録できることであり、データ量が増えるほどその利点が明確になります。疎インデックスはスペースを占有しないため、primary.idx 内のインデックスデータは常にメモリに常駐します。
インデックス粒度#
インデックス粒度は、データの長さに基づいてデータをマークし、最終的にデータを index_granularity の粒度で複数の間隔の小さなセグメントにマークします。MergeTree は MarkRange を使用して具体的な区間を表し、start と end を使用してその具体的な範囲を示します。このパラメータは一次インデックスだけでなく、データマークとデータファイルにも影響を与えます。
一次インデックスだけではクエリ作業を完了できず、データマークを利用してデータを特定する必要があります。データファイルも index_granularity の間隔粒度に従って圧縮データブロックを生成します。
インデックスデータの生成ルール#
MergeTree は index_granularity 行のデータごとにインデックスレコードを生成し、そのインデックス値は宣言された主キー列に基づいて取得されます。疎インデックスのストレージは非常にコンパクトで、インデックス値は前後に緩やかに、主キー列の順序で密接に配置されます。
インデックスのクエリプロセス#
MarkRange は ClickHouse でマーク区間を定義するためのオブジェクトです。MergeTree は index_granularity の間隔に基づいて、完全なデータを複数の小さな間隔データセグメントに分割し、具体的なデータセグメントは 1 つの MarkRange です。
インデックスクエリプロセスは大きく 3 つのステップに分けられます:
(1) クエリ条件区間の生成:まず、クエリ条件を条件区間に変換します。単一の値のクエリ条件であっても、区間の形式に変換されます。
(2) 再帰的な交差判断:再帰的な形式で、MarkRange の数値区間と条件区間の交差判断を順次行います。
- 交差が存在しない場合、直接剪定アルゴリズムを使用してこの全体の MarkRange を最適化します
- 交差が存在し、MarkRange のステップが 8 より大きい場合(end-start)、この区間はさらに 8 つのサブ区間に分割され(merge_tree_coarse_index_granularity によって指定)、このルールを繰り返し、再帰的な交差判断を続けます
- 交差が存在し、MarkRange が分解できない場合(ステップが 8 未満)、MarkRange を記録して返します
(3) MarkRange 区間の統合:最終的に一致した MarkRange をまとめて、その範囲を統合します。
MergeTree は再帰的な形式で区間を下に分割し続け、最終的に MarkRange を最も細かい粒度に特定し、後続のデータ読み取り時にデータスキャンの範囲を最小化できるようにします。
二次インデックス#
二次インデックスはスキップインデックスとも呼ばれ、データの集約情報によって構築されます。インデックスの種類によって、集約情報の内容も異なります。目的は、クエリ時にデータスキャンの範囲を減少させることです。デフォルトでは無効になっており、allow_experimental_data_skipping_indices を設定する必要があります。
SET allow_experimental_data_skipping_indices = 1
二次インデックスは CREATE 文内で定義する必要があり、タプルや式の形式で宣言をサポートします。その完全な定義構文は以下の通りです:
INDEX index_name expr TYPE index_type (...) GRANULARITY granularity
対応するインデックス(skp_idx_[column].idx)とマークファイル(skp_idx_[column].mrk)が追加生成されます。
granularity と index_granularity の関係#
スキップインデックスにおいて、index_granularity はデータの粒度を定義し、granularity は集約情報の要約粒度を定義します(一行のスキップインデックスがスキップできる index_granularity 区間のデータ数)。
スキップインデックスの種類#
MergeTree は 4 種類のスキップインデックスをサポートしており、それぞれ minmax、set、ngrambf_v1、および tokenbf_v1 です。
スキップインデックスの使用法:
(1) minmax: 一組のデータ内の最小および最大の極値を記録し、そのインデックスの役割はパーティションディレクトリの minmax インデックスに似ており、無駄なデータ区間を迅速にスキップできます。
INDEX a ID TYPE minmax GRANULARITY 5
(2) set: 宣言されたフィールドまたは式の値(ユニーク値、重複なし)を記録します。完全な形式は set (max_rows) で、1 つの index_granularity 内でインデックスが記録する最大データ行数を示します。max_rows=0 の場合は制限なしを意味します。
INDEX b (length(ID) * 8) TYPE set(100) GRANULARITY 5
(3) ngrambf_v1: データフレーズのブロームフィルターを記録し、string および fixedstring データ型をサポートします。また、in、notIn、like、equals、notEquals に対してのみ有効です。
INDEX c (ID, Code) TYPE ngrambf_v1(3,256,2,0) GRANULARITY 5
- n: トークンの長さで、n の長さに基づいてデータをトークンフレーズに分割します
- size_of_bloom_filter_in_bytes: ブロームフィルターのサイズ
- number_of_hash_functions: ブロームフィルター内で使用されるハッシュ関数の数
- random_seed: ハッシュ関数のランダムシード
(4) tokenbf_v1: ngrambf_v1 の変種で、トークンの処理方法が変更され、非文字および数字の文字列に基づいてトークンを分割します。
INDEX d ID TYPE tokenbf_v1(256,2,0) GRANULARITY 5
データストレージ#
各列の独立ストレージ#
各列に対応するフィールドには、対応する.bin データファイルがあり、パーティションディレクトリの形式で組織的に保存されています。
列ごとの独立ストレージの設計の利点:
- データ圧縮をより良く行える(同じタイプのデータをまとめることで圧縮に優しい)
- データスキャンの範囲を最小化できる
データは圧縮されており、現在 LZ4、ZSTD、Multiple、Delta などのアルゴリズムをサポートしており、デフォルトでは LZ4 が使用されます。次に、データは ORDER BY の宣言に従ってソートされます。最後に、データは圧縮データブロックの形式で.bin ファイルに書き込まれます。
圧縮データブロック#
圧縮データブロックは、ヘッダー情報と圧縮データの 2 つの部分で構成されています。ヘッダー情報は固定で 9 バイトで表され、具体的には 1 つの UInt8(1 バイト)整数と 2 つの UInt32(4 バイト)整数で構成され、使用される圧縮アルゴリズムのタイプ、圧縮後のデータサイズ、および圧縮前のデータサイズをそれぞれ示します。
.bin 圧縮ファイルは、複数の圧縮データブロックで構成されており、各圧縮データブロックのヘッダー情報は CompressionMethod_CompressedSize_UncompressedSize の公式に基づいて生成されます。
clickhouse-compressor ツールを使用して、.bin ファイル内の圧縮データの統計情報を照会できます。
各圧縮データブロックの体積は、圧縮前のデータバイトサイズに基づいて、64KB〜1MB の範囲で厳密に制御されており、上限と下限は min_compress_block_size(65536)および max_compress_block_size(1048576)によって指定されます。圧縮データブロックの最終的なサイズは、間隔内(index_granularity)のデータの実際のサイズに関連しています。
MergeTree のデータの具体的な書き込みプロセスは、インデックス粒度に従って、バッチごとにデータを取得して処理します。未圧縮サイズを size とすると、全体の書き込みプロセスは以下の原則に従います:
(1)単一のバッチデータ size<64KB: 次のバッチデータを取得し続け、size>64KB になるまで累積し、次の圧縮データブロックを生成します。
(2)単一のバッチデータ 64KB <=size<=1MB: 次の圧縮データブロックを直接生成します。
(3)単一のバッチデータ size>=1MB: まず 1MB のサイズで切断し、次の圧縮データブロックを生成します。残りのデータは上記のルールに従って実行され、この時、1 つのバッチデータが複数の圧縮データブロックを生成することが発生します。
.bin ファイルに圧縮データブロックを導入する目的は、少なくとも以下の 2 つがあります:
一、データが圧縮された後、データサイズを効果的に削減し、ストレージスペースを減少させ、データ転送効率を加速しますが、データの圧縮と解凍の動作は、追加のパフォーマンス損失をもたらすため、圧縮データのサイズを制御し、パフォーマンス損失と圧縮率の間でバランスを求める必要があります。
二、特定の列データを読み取る際、まず圧縮データをメモリにロードして解凍する必要があります。圧縮データブロックを使用することで、全体の.bin を読み取ることなく、読み取り粒度を圧縮データブロックのレベルに下げることができ、データ読み取りの範囲をさらに縮小できます。
データマーク#
データマークの生成原則#
データマークとインデックス区間は整合しており、両方とも index_granularity の粒度間隔に従います。したがって、インデックス区間のインデックス番号を通じて、対応するデータマークを直接見つけることができます。
データマークファイルと.bin ファイルは 1 対 1 で対応しており、データが.bin ファイル内のオフセット情報を記録するために使用されます。
1 行のマークデータはタプルで表され、タプル内には 2 つの整数値のオフセット情報が含まれています。それぞれ、このデータ区間内で、対応する.bin 圧縮ファイル内の圧縮データブロックの開始オフセットと、このデータ圧縮ブロックを解凍した後の未圧縮データの初期オフセットを示します。
マークデータは一次インデックスデータとは異なり、常駐メモリにはできず、LRU キャッシュ戦略を使用してその使用速度を加速します。
データマークの作業方式#
データを読み取る際、マークデータの位置情報を通じて必要なデータを見つける必要があります。全体の検索プロセスは、圧縮データブロックの読み取りとデータの読み取りの 2 つのステップに大別されます。
MergeTree は、圧縮データブロックをどのように特定し、データを読み取るか:
(1)圧縮データブロックの読み取り:特定の列データをクエリする際、MergeTree は全体の.bin ファイルを一度にロードする必要はなく、必要に応じて特定の圧縮データブロックのみをロードできます。この機能は、マークファイルに保存された圧縮ファイル内のオフセットを利用する必要があります。
(2)データの読み取り:解凍されたデータを読み取る際、MergeTree は一度に全体の解凍データをスキャンする必要はなく、必要に応じて index_granularity の粒度から特定の小さなセグメントをロードできます。
パーティション、インデックス、マーク、圧縮データの協調的な要約#
書き込みプロセス#
データ書き込みの最初のステップは、パーティションディレクトリを生成することであり、各バッチデータの書き込みに伴い、新しいパーティションディレクトリが生成されます。その後、同じパーティションに属するディレクトリは、ルールに従ってマージされます。次に、index_granularity インデックス粒度に従って、primary.idx 一次インデックスまたは二次インデックスが生成され、各列フィールドの.mrk データマークと.bin 圧縮データファイルが生成されます。
クエリプロセス#
データクエリの本質は、データ範囲を継続的に縮小するプロセスと見なすことができます。理想的な状況では、MergeTree は最初にパーティションインデックス、一次インデックス、二次インデックスを利用して、データスキャン範囲を最小限に縮小できます。次に、データマークを利用して、解凍および計算するデータ範囲を最小限に縮小します。
クエリ文が where 条件を指定していない場合、または where 条件を指定しているが条件がインデックス(パーティションインデックス、一次インデックス、二次インデックス)に一致しない場合、MergeTree は事前にデータ範囲を縮小できません。その後のデータクエリでは、すべてのパーティションディレクトリとディレクトリ内のインデックスセグメントの最大区間をスキャンします。データ範囲を縮小できない場合でも、MergeTree はデータマークを利用して、マルチスレッド形式で複数の圧縮データブロックを同時に読み取ることでパフォーマンスを向上させることができます。
データマークと圧縮データブロックの対応関係#
圧縮データブロックの分割は、1 つの間隔(index_granularity)内のデータサイズに関連しており、各圧縮データブロックの体積は 64KB〜1MB の範囲で厳密に制御されています。1 つの間隔のデータは、1 行のデータマークを生成します。したがって、1 つの間隔内のデータの実際のバイトサイズに基づいて、データマークと圧縮データブロックの間には 3 つの異なる対応関係が生じます。
多対一#
複数のデータマークが 1 つの圧縮データブロックに対応します。1 つの間隔内のデータの未圧縮サイズ size が 64KB 未満の場合、この対応関係が発生します。
一対一#
1 つのデータマークが 1 つの圧縮データブロックに対応します。1 つの間隔内のデータの未圧縮サイズ size が 64KB 以上 1MB 未満の場合、この関係が発生します。
一対多#
1 つのデータマークが複数の圧縮データブロックに対応します。1 つの間隔内の圧縮データが圧縮サイズ size が 1MB を超える場合、この関係が発生します。
第 7 章 MergeTree シリーズテーブルエンジン#
現在、ClickHouse では、特徴に応じてテーブルエンジンを大きく 6 つのシリーズに分けることができます。それぞれ、MergeTree、外部ストレージ、メモリ、ファイル、インターフェース、その他であり、各シリーズのテーブルエンジンには独自の特徴と使用シナリオがあります。
MergeTree#
MergeTree は、ファミリーシリーズの最も基本的なテーブルエンジンであり、データパーティション、一次インデックス、二次インデックスなどの機能を提供します。
データ TTL#
TTL はデータの生存時間を示します。MergeTree では、特定の列フィールドまたはテーブル全体に TTL を設定できます。時間が到達すると、列フィールドレベルの TTL の場合はその列のデータが削除され、テーブルレベルの TTL の場合はテーブル全体のデータが削除されます。列レベルとテーブルレベルの TTL が同時に設定されている場合は、先に期限が到達したものが優先されます。
TTL は、特定の DateTime または Date 型のフィールドに依存し、この時間フィールドの INTERVAL 操作を通じて TTL の期限を示します。
列レベル TTL#
列レベルの TTL を設定する場合、テーブルフィールドを定義する際に TTL 式を宣言する必要があります。主キー列には TTL を宣言できません。
create table ttl_table_v1 (
id String,
create_time DateTime,
code String TTL create_time + INTERVAL 10 SECOND,
type UInt8 TTL create_time + INTERVAL 10 SECOND
)
ENGINE = MergeTree
PARTITION BY toYYYYMM(create_time)
ORDER BY id
現在、ClickHouse は列レベル TTL をキャンセルする方法を提供していません。
テーブルレベル TTL#
テーブル全体に TTL を設定する場合、MergeTree のテーブルパラメータに TTL 式を追加する必要があります。
create table ttl_table_v2 (
id String,
create_time DateTime,
code String TTL create_time + INTERVAL 10 SECOND,
type UInt8 TTL create_time + INTERVAL 10 SECOND
)
ENGINE = MergeTree
PARTITION BY toYYYYMM(create_time)
ORDER BY create_time
TTL create_time + INTERVAL 1 DAY
テーブルレベル TTL も現在キャンセルする方法はありません。
TTL の実行メカニズム#
TTL 式が設定されている場合、データが書き込まれる際、データパーティション単位で、各パーティションディレクトリに ttl.txt という名前のファイルが生成され、JSON 設定で TTL に関する情報が保存されます:
- columns は列レベルの TTL 情報を保存します
- table はテーブルレベルの TTL 情報を保存します
- min と max は、現在のデータパーティション内の TTL 指定日付フィールドの最小値と最大値を、それぞれ INTERVAL 式計算後のタイムスタンプと保存します。
大まかな処理ロジックは以下の通りです:
- MergeTree はパーティションディレクトリ単位で、ttl.txt ファイルを通じて期限切れの時間を記録し、以降の判断基準とします。
- データのバッチを書き込むたびに、INTERVAL 式の計算結果に基づいてこのパーティションに ttl.txt ファイルが生成されます。
- MergeTree がパーティションをマージする際にのみ、TTL 期限切れデータの削除ロジックがトリガーされます。
- 削除するパーティションを選択する際、貪欲アルゴリズムが使用され、できるだけ早く期限切れになるパーティションを見つけ、かつ年齢が最も古いパーティション(マージ回数が多く、MaxBlockNum が大きい)を見つけます。
- もしあるパーティション内の特定の列データが TTL の期限切れによりすべて削除された場合、マージ後に生成される新しいパーティションディレクトリにはその列フィールドのデータファイル(.bin および.mrk)が含まれません。
注意: - TTL のデフォルトのマージ頻度は、MergeTree の merge_with_ttl_timeout パラメータによって制御され、デフォルトは 86400 秒、つまり 1 日です。これは専用の TTL タスクキューを維持します。MergeTree の通常のマージタスクとは異なり、この値が小さすぎるとパフォーマンス損失をもたらす可能性があります。
- TTL マージを受動的にトリガーするだけでなく、optimize コマンドを使用して強制的にマージをトリガーすることもできます。
optimize table table_name
optimize table table_name FINAL # すべてのパーティションをマージするトリガー
- ClickHouse は現在 TTL 宣言を削除する方法を提供していませんが、グローバル TTL マージタスクの開始と停止を制御する方法を提供しています。
SYSTEM STOP/START TTL MERGES
マルチパスストレージポリシー#
MergeTree はカスタムストレージポリシーの機能を実装しており、データパーティションを最小移動単位として、パーティションディレクトリを複数のディスクディレクトリに書き込むことをサポートしています。
現在、3 種類のストレージポリシーがあります:
- デフォルトポリシー:MergeTree の元のストレージポリシーで、特別な設定は不要で、すべてのパーティションは config.xml 設定内の path で指定されたパスに自動的に保存されます。
- JBOD ポリシー:このポリシーは、サーバーに複数のディスクがマウントされているが RAID が構成されていないシナリオに適しています。JBOD はラウンドロビンポリシーで、INSERT または MERGE を実行するたびに、新しいパーティションが各ディスクにラウンドロビンで書き込まれます。このポリシーの効果は RAID 0 に似ており、単一のディスクの負荷を軽減し、特定の条件下でデータの並行読み書き性能を向上させることができます。単一のディスクに障害が発生した場合、JBOD ポリシーで書き込まれたこの部分のデータは失われます。
- HOT/COLD ポリシー:このポリシーは、サーバーに異なるタイプのディスクがマウントされているシナリオに適しています。ストレージディスクを HOT と COLD の 2 つの領域に分けます。HOT 領域は SSD などの高性能ストレージメディアを使用し、アクセス性能を重視します。COLD 領域は HDD などの高容量ストレージメディアを使用し、アクセス経済性を重視します。データが閾値に達すると、データは自動的に COLD 領域に移動します。また、各領域内でも複数のディスクを定義できるため、単一の領域の書き込みプロセスでも JBOD ポリシーを適用できます。
ReplacingMergeTree#
主キーを持っていますが、その主キーにはユニークキーの制約がありません。つまり、複数行のデータの主キーが同じであっても、正常に書き込むことができます。ReplacingMergeTree はデータの重複を排除するために設計されており、パーティションをマージする際に重複データを削除できます。
ソートキー ORDER BY で宣言された式は、データが重複しているかどうかを判断する基準となります。
ReplacingMergeTree はパーティション単位で重複データを削除します。同じデータパーティション内の重複データのみが削除され、異なるデータパーティション間の重複データは削除されません。
処理ロジック:
- ORDER BY ソートキーを重複データの唯一のキーとして使用します
- パーティションをマージする際にのみ重複データの削除ロジックがトリガーされます
- データパーティション単位で重複データを削除します。パーティションがマージされると、同じパーティション内の重複データが削除され、異なるパーティション間の重複データは削除されません。
- データの重複排除時、パーティション内のデータは ORDER BY に基づいてソートされているため、隣接する重複データを見つけることができます。
- データの重複排除ロジックには 2 つの戦略があります:
- ver バージョン番号が設定されていない場合、同じグループの重複データの最後の行を保持します
- ver バージョン番号が設定されている場合、同じグループの重複データの中で ver フィールドの値が最大の行を保持します。
SummingMergeTree#
パーティションをマージする際に、事前に定義された条件に従ってデータを集約し、同じグループ内の複数行のデータを 1 行にまとめることができ、これによりデータ行が減少し、後続の集約クエリのオーバーヘッドが低減されます。
処理ロジック:
- ORDER BY ソートキーを集約データの条件キーとして使用します
- パーティションをマージする際にのみ集約ロジックがトリガーされます
- データパーティション単位でデータを集約します。パーティションがマージされると、同じデータパーティション内の集約キーが同じデータが集約されますが、異なるパーティション間のデータは集約されません。
- エンジンを定義する際に集約列(主キー以外の数値型フィールド)を指定した場合、これらの列フィールドを SUM 集約します。指定しない場合は、すべての主キー以外の数値型フィールドを集約します。
- データ集約時、パーティション内のデータは ORDER BY でソートされているため、隣接していて同じ集約キーを持つデータを見つけることができます。
- 集約データ時、同じパーティション内で同じ集約キーの複数行のデータが 1 行にまとめられます。その際、集約フィールドは SUM 計算され、非集約フィールドは最初の行の値が使用されます。
- ネスト構造をサポートしますが、列フィールド名は Map サフィックスで終わる必要があります。ネストタイプでは、デフォルトで最初のフィールドが集約キーとして使用されます。最初のフィールド以外で、Key、Id、Type でサフィックスが終わるフィールド名は、最初のフィールドと組み合わせてキーに適合します。
AggregatingMergeTree#
処理ロジック:
- ORDER BY ソートキーを集約データの条件キーとして使用します
- AggregateFunction フィールドタイプを使用して、集約関数のタイプと集約するフィールドを定義します
- パーティションをマージする際にのみ集約計算ロジックがトリガーされます
- データパーティション単位でデータを集約します。パーティションがマージされると、同じデータパーティション内の集約キーが同じデータが集約計算されますが、異なるパーティション間のデータは計算されません。
- データ計算時、パーティション内のデータは ORDER BY でソートされているため、隣接していて同じ集約キーを持つデータを見つけることができます。
- 集約データ時、同じパーティション内で同じ集約キーの複数行のデータが 1 行にまとめられます。主キー、非 AggregateFunction 型フィールドは、最初の行の値が使用されます。
- AggregateFunction 型のフィールドはバイナリ形式で保存され、データを書き込む際には
*State
関数を呼び出す必要があります。データをクエリする際には、対応する*Merge
関数を呼び出す必要があります。その際、*
は定義時に使用される集約関数を示します。 - AggregatingMergeTree は通常、マテリアライズドビューのテーブルエンジンとして使用され、通常の MergeTree と組み合わせて使用されます。
第 9 章 データクエリ#
ClickHouse は SQL 文の解析が大文字と小文字を区別するため、SELECT a と SELECT A は異なる意味を持ちます。
WITH 句#
ClickHouse は CTE(Common Table Expression、共通テーブル式)をサポートしており、クエリ文の表現を強化します。この形式は、文の可読性と保守性を大幅に向上させることができます。
CTE は WITH 句を通じて表現され、現在 4 つの使用法がサポートされています。
- 変数の定義
変数を定義でき、これらの変数は後続のクエリ句で直接アクセスできます。
WITH 10 AS start
SELECT number FROM system.numbers
WHERE number > start
LIMIT 5
- 関数の呼び出し
WITH SUM(data_uncompressed_bytes) AS bytes
SELECT database, formatReadableSize(bytes) AS format FROM system.columns
GROUP BY database
ORDER BY bytes DESC
- サブクエリの定義
WITH(
SELECT SUM(data_uncompressed_bytes) FROM system.columns
) AS total_bytes
SELECT database, (SUM(data_uncompressed_bytes)/total_bytes) * 100 AS database_disk_usage
FROM system.columns
GROUP BY database
ORDER BY database_disk_usage DESC
WITH 内でサブクエリを使用する際は注意が必要です。このクエリ文は 1 行のデータしか返すことができず、結果セットが 1 行を超えると例外がスローされます。
4. サブクエリ内で WITH を再利用
WITH(round(database_disk_usage)) AS database_disk_usage_v1
SELECT database, database_disk_usage, database_disk_usage_v1
FROM (
WITH (SELECT SUM(data_uncompressed_bytes) FROM system.columns) AS total_bytes
SELECT database, (SUM(data_uncompressed_bytes)/total_bytes)*100 AS database_disk_usage
FROM system.columns
GROUP BY database
ORDER BY database_disk_usage DESC
)
FROM 句#
FROM 句はデータをどこから読み取るかを示します。現在、3 つの形式がサポートされています。
- データテーブルからの取得
SELECT watch_id FROM hits_v1
- サブクエリからの取得
SELECT max_watch_id from (SELECT MAX(watch_id) from hits_v1)
- テーブル関数からの取得
SELECT number FROM numbers(5)
FROM 句の後に Final 修飾子を使用できます。これは、CollapsingMergeTree や VersionedCollapsingMergeTree などのテーブルエンジンと組み合わせてクエリ操作を行い、クエリプロセス中にマージを強制します。ただし、Final 修飾子はクエリパフォーマンスを低下させるため、できるだけ使用を避けるべきです。
SAMPLE 句#
SAMPLE 句はデータサンプリング機能を実現し、クエリがすべてのデータではなくサンプリングデータのみを返すようにし、負荷を効果的に減少させます。SAMPLE 句のサンプリングメカニズムは冪等設計であり、データが変化しない限り、同じサンプリングルールを使用すると常に同じデータが返されるため、この特性は近似クエリ結果を受け入れることができるシナリオで非常に適しています。
SAMPLE 句は MergeTree シリーズエンジンのデータテーブルにのみ使用でき、create table 時に sample by サンプリング式を宣言する必要があります。
CREATE TABLE hits_v1 (
counterid UInt64,
EventDate DATE,
UserID UInt64
) ENGINE = MergeTree()
PARTITION BY toYYYYMM(EventDate)
ORDER BY (counterid, intHash32(UserID))
SAMPLE BY intHash32(UserID)
サンプルキーを宣言する際には注意が必要です:
- sample by で宣言された式は、主キーの宣言内にも含まれている必要があります。
- サンプルキーは Int 型でなければならず、そうでない場合、ClickHouse は create table 操作時にエラーを報告しませんが、クエリ時に例外が発生します。
句の 3 つの使用法:
- SAMPLE factor
SAMPLE factor は因子係数によるサンプリングを示し、factor はサンプリング因子を示します。factor は 0〜1 の間の小数をサポートします。factor が 0 または 1 に設定されている場合、データサンプリングを行わないのと同じ効果があります。 - SAMPLE rows
SAMPLE rows はサンプル数によるサンプリングを示し、rows は少なくともサンプリングする行数を示します。rows は 1 より大きい整数でなければなりません。rows の値がテーブル内のデータの総行数を超える場合、効果は rows=1(つまりサンプリングを使用しない)と同じです。 - SAMPLE factor OFFSET n
因子係数とオフセットによるサンプリングを示し、factor はサンプリング因子、n はどれだけデータをオフセットしてからサンプリングを開始するかを示します。これら 2 つの値は 0〜1 の間の小数です。
ARRAY JOIN 句#
ARRAY JOIN 句は、データテーブル内で配列またはネスト型のフィールドと JOIN 操作を行い、1 行の配列を複数行に展開することを許可します。現在、inner と left の 2 つの JOIN 戦略がサポートされています:
-
inner array join
ARRAY JOIN はデフォルトで INNER JOIN 戦略を使用します。
データは value 配列に基づいて複数行に展開され、空の配列は除外されます。 -
LEFT ARRAY JOIN
空の配列を除外せず、複数の配列フィールドに対して array join 操作を行う場合、クエリの計算ロジックは行を結合するのではなく、行をマージします。
JOIN 句#
構文は接続精度と接続タイプの 2 つの部分を含みます。
接続精度は ALL、ANY、ASOF の 3 種類に分かれ、接続タイプは外部接続、内部接続、交差接続の 3 種類に分かれます。
接続精度#
接続精度は、JOIN クエリがデータを接続する際に使用する戦略を決定します。現在、ALL、ANY、ASOF の 3 種類がサポートされています。デフォルトは ALL で、join_default_strictness 設定パラメータを通じてデフォルトの接続精度タイプを変更できます。
ALL#
左テーブル内の 1 行のデータが、右テーブル内の複数行のデータと接続マッチする場合、右テーブル内のすべての接続データが返されます。接続マッチの判断基準は、左テーブルと右テーブル内のデータが接続キーの値が完全に等しいことです。つまり、left.key=right.key と同じです。
ANY#
左テーブル内の 1 行のデータが、右テーブル内の複数行のデータと接続マッチする場合、右テーブル内の最初の行の接続データが返されます。ANY と ALL の接続マッチの判断基準は同じです。
ASOF#
ASOF はあいまい接続であり、接続キーの後にあいまい接続のマッチ条件 asof_column を追加することを許可します。
SELECT a.id, a.name, b.rate, a.time, b.time FROM join_tab1 AS a ASOF INNER JOIN join_tb2 AS B ON a.id=b.id AND a.time = b.time
ここで、a.id = b.id は通常の接続キーであり、a.time=b.time は asof_column あいまい接続条件であり、意味はa.time>= b.time
と同じです。
注意:asof_column は整数型でなければならず、浮動小数点型や日付などの順序付きシーケンスのデータ型でなければなりません。asof_column はデータテーブル内のユニークフィールドであってはならず、つまり接続キー(JOIN_KEY)と asof_column は同じフィールドであってはなりません。
接続タイプ#
接続タイプは、JOIN クエリが左右の 2 つのデータ集合を組み合わせる際に使用する戦略を決定し、交差、和、デカルト積、またはその他の形式の結果を生成します。
INNER#
INNER JOIN は内部接続を示し、クエリ時に左テーブルを基にデータを順次走査し、右テーブルから左側に接続される行を見つけます。左テーブルと右テーブルの 2 つのデータ集合の交差部分のみを返し、残りの部分はすべて排除されます。
OUTER#
外部接続を示し、左外接続(LEFT)、右外接続(RIGHT)、全外接続(FULL)の 3 つの形式にさらに細分化できます。接続形式によって、返されるデータ集合のロジックは異なります。
- LEFT
左外接続クエリを実行する際、左テーブルを基に逐次データを走査し、右テーブルから左側に接続される行を見つけて属性を補完します。右テーブルで接続される行が見つからない場合、対応するフィールドデータ型のデフォルト値で埋められます。言い換えれば、左接続クエリにおいては、左テーブルのデータは常にすべて返されます。 - RIGHT
右外接続クエリの効果は左接続と正反対であり、右テーブルのデータは常にすべて返され、左テーブルで接続されないデータはデフォルト値で補完されます。
右外接続クエリを実行する際の内部の実行ロジックは大まかに以下の通りです:
(1) 内部で INNER JOIN の内部接続クエリを実行し、交差部分を計算しながら、右テーブル内で接続されなかったデータ行を記録します。
(2) 接続されなかったデータ行を交差の末尾に追加します。
(3) 追加されたデータの中で左テーブルに属する列フィールドはデフォルト値で補完されます。 - FULL
全外接続の内部の実行ロジックは大まかに以下の通りです:
(1) 内部で LEFT JOIN のクエリを実行し、左外接続の過程で右テーブル内で接続されたデータ行を記録します。
(2) 右テーブル内で接続されたデータ行を記録することで、接続されなかったデータ行を取得します。
(3) 右テーブル内で接続されなかったデータを結果セットに追加し、左テーブルに属する列フィールドはデフォルト値で補完されます。
CROSS#
交差接続を示し、左テーブルと右テーブルの 2 つのデータ集合のデカルト積を返します。
多テーブル接続#
複数のデータテーブルの接続クエリを実行する際、ClickHouse はそれらを 2 つずつ接続する形式に変換します。
注意事項#
JOIN クエリに関する注意事項
- パフォーマンスについて
JOIN クエリのパフォーマンスを最適化するためには、まず左大右小の原則に従うべきです。次に、JOIN クエリは現在キャッシュのサポートがありません。最後に、大量のディメンション属性を補完するクエリシナリオでは、JOIN クエリの代わりに辞書を使用することをお勧めします。 - 空値戦略と省略形式について
接続クエリの空値はデフォルト値で埋められます。接続クエリの空値戦略は join_use_nulls パラメータによって指定され、デフォルトは 0 です。パラメータ値が 0 の場合、空値はデータ型のデフォルト値で埋められ、パラメータ値が 1 の場合、空値は Null で埋められます。
WHERE および PREWHERE 句#
WHERE 句は条件式に基づいてデータフィルタリングを実現します。フィルタ条件がちょうど主キー列である場合、インデックスを利用してクエリを加速できるため、WHERE 句はクエリ文がインデックスを使用するかどうかの判断基準となります。
PREWHERE は現在 MergeTree シリーズのテーブルエンジンでのみ使用でき、WHERE の最適化の一種と見なすことができ、WHERE と同様にデータをフィルタリングするために使用されます。異なる点は、PREWHERE を使用する際、最初に SELECT で宣言された列フィールドのみを読み取り、残りの属性を補完します。いくつかの状況では、PREWHERE は WHERE よりも処理するデータ量が少なく、パフォーマンスが高くなります。
以下の状況では WHERE は自動的に最適化されません:
- 定数式を使用した場合
- デフォルト値が ALIAS 型のフィールドを使用した場合
- arrayJoin、globalIn、globalNotIn、または indexHint を含むクエリ
- SELECT クエリの列フィールドと WHERE 述語が同じである場合
- 主キー列を使用した場合
PREWHERE を使用して主キーをクエリする場合、最初に疎インデックスを通じてデータ区間をフィルタリングし、次に prewhere で指定された条件列を読み取ってさらにフィルタリングします。これにより、データ区間の末尾を切り取る可能性があり、index_granularity 粒度のデータ範囲を返すことができます。それでも、他の状況で移動述語がもたらすパフォーマンス向上に比べて、この効果は限られています。
GROUP BY 句#
集約クエリを示します。これは ClickHouse の卓越したパフォーマンスを最も際立たせる部分であり、GROUP BY の後に宣言された式は集約キーまたは key となり、データは集約キーに基づいて集約されます。
特定の状況では、any、max、min などの集約関数を利用して集約キー以外の列フィールドにアクセスできます。
WITH ROLLUP#
ROLLUP は集約キーに基づいてデータを右から上に巻き上げ、小計と合計を生成します。
WITH CUBE#
CUBE は立方体モデルのように、集約キー間のすべての組み合わせに基づいて小計情報を生成します。
WITH TOTALS#
すべてのデータに対して集約関数に基づいて合計を生成します。
HAVING 句#
GROUP BY と同時に使用する必要があり、単独では使用できません。集約計算後にデータを二次フィルタリングすることができます。
ORDER BY 句#
ORDER BY 句は、ソートキーを宣言することで、クエリデータの返却順序を指定します。MergeTree で ORDER BY を指定すると、データは各パーティション内で定義されたルールに従ってソートされます。これはパーティション内の局所的なソートです。クエリ時にデータが複数のパーティションを越える場合、返却順序は予測できず、毎回のクエリで返却される順序が異なる可能性があります。この場合、データが常に期待される順序で返されるようにするには、ORDER BY 句を使用して全体の順序を指定する必要があります。
ORDER BY を使用する際、複数のソートキーを定義でき、各ソートキーの後には ASC(昇順)または DESC(降順)を続けてソート順序を決定します。デフォルトは ASC です。
- NULLS LAST NULL 値は最後に配置され、デフォルトの動作です。
- NULLS FIRST NULL 値は最初に配置されます。
LIMIT BY 句#
LIMIT BY は ORDER BY の後、LIMIT の前に実行され、指定されたパーティションから最大 n 行のデータを返します。TOP N のクエリシナリオでよく使用されます。
LIMIT 句#
LIMIT 句は、指定された前 N 行のデータを返すために使用され、ページネーションシナリオでよく使用されます。3 つの構文形式は以下の通りです:
limit n
limit n offset m
limit m,n
LIMIT 句を使用する際、データが複数のパーティションを越える場合、ORDER BY で全体の順序を指定していない限り、各 LIMIT クエリの返却データが異なる可能性があります。
SELECT 句#
クエリ文が最終的にどの列フィールドまたは式を返すかを決定します。SELECT は SQL 文の最初の位置にありますが、上記のすべての句の後に実行されます。他の句が実行された後、SELECT は選択されたフィールドまたは式を各行データに適用します。*
ワイルドカードを使用すると、データテーブルのすべてのフィールドが返されます。
DISTINCT 句#
重複データを除去するために使用されます。GROUP BY と同時に使用でき、相互補完的であり、排他的ではありません。
UNION ALL 句#
UNION ALL 句は、左右の 2 つのサブクエリを結合し、結果を一緒に返します。一度のクエリで、複数回 UNION ALL を宣言して複数のサブクエリを結合できますが、UNION ALL は他の句を直接使用できません。
クエリ SQL 実行計画#
(1)ClickHouse サービスのログを DEBUG または TRACE レベルに設定することで、EXPLAIN クエリを実現し、SQL の実行ログを分析できます。
(2)SQL クエリを実行しない限り、CH は計画ログを印刷できないため、テーブルのデータ量が非常に大きい場合は LIMIT 句を利用して返却数量を減少させるのが最適です。
(3)SELECT * 全フィールドクエリは使用しないでください。
(4)さまざまなインデックス(パーティションインデックス、一次インデックス、二次インデックス)をできるだけ利用し、全テーブルスキャンを回避します。
clickhouse-client -h 127.0.0.1 -u uts --port 9100 --password ~Uts2020db --send_logs_level=trace <<< "select timestamp,sip4,sip6,sport,dip4,dip6,dport,protocol,app_proto,app_desc_cn,dmac,s_card_name,s_device_hash,tx_bytes,tx_pkts,rx_bytes,rx_pkts,isIPv4,direct,first_time,last_time,sid from uts.storage_session prewhere sid global in (select sid from uts.storage_session prewhere timestamp > 1698826475 and timestamp <= 1699337215 and msgtype = 12 order by timestamp desc limit 10 offset 2357) order by timestamp desc" > /dev/null
第 10 章 レプリカとシャーディング#
クラスターはレプリカとシャーディングの基盤であり、ClickHouse のサービストポロジーを単一ポイントから複数ノードに拡張しますが、Hadoop エコシステムの一部のシステムのように、すべてのノードが単一の大きなクラスターを構成することを要求するわけではありません。ClickHouse のクラスター構成は非常に柔軟で、ユーザーはすべてのノードを単一のクラスターに構成することも、ビジネスの要求に応じてノードを複数の小さなクラスターに分割することもできます。各小さなクラスター領域内では、ノード、パーティション、レプリカの数は異なる場合があります。
機能的には、ClickHouse クラスターの作業は論理レベルに対してより多くのものであり、クラスターは複数のノードのトポロジー関係を定義し、これらのノードは後続のサービスプロセスで協調して作業する可能性がありますが、実行レベルの具体的な作業はレプリカとシャーディングに委ねられます。
レプリカとシャーディングの区別方法
1 つ目:データレベルから分けると、ClickHouse の N 個のノードが 1 つのクラスターを構成し、クラスターの各ノードには結果が同じデータテーブル Y があります。N1 の Y と N2 の Y のデータが完全に異なる場合、N1 と N2 は互いにシャーディングです。データが完全に同じである場合、互いにレプリカです。
2 つ目:機能的な役割の観点から区別すると、レプリカの主な目的はデータの損失を防ぎ、データストレージの冗長性を高めることです。一方、シャーディングの主な目的はデータの水平分割を実現することです。
データレプリカ#
ReplicatedMergeTree レプリケーションテーブルシリーズエンジンを使用する場合にのみ、レプリカの機能を適用できます。また、ReplicatedMergeTree のデータテーブルはレプリカです。
レプリカの特徴#
データレプリカの主要な実装媒体である ReplicatedMergeTree は、設計上いくつかの顕著な特徴を持っています。
- ZooKeeper に依存:INSERT および ALTER クエリを実行する際、ReplicatedMergeTree は ZooKeeper の分散協調機能を利用して、複数のレプリカ間の同期を実現しますが、レプリカをクエリする際には ZooKeeper を使用する必要はありません。
- テーブルレベルのレプリカ:レプリカはテーブルレベルで定義されているため、各テーブルのレプリカ設定はその実際のニーズに応じて個別に定義できます。これにはレプリカの数やクラスター内でのレプリカの分布位置などが含まれます。
- マルチマスターアーキテクチャ:任意のレプリカで INSERT および ALTER クエリを実行でき、その効果は同じです。これらの操作は ZooKeeper の協調機能を通じて各レプリカに配信され、ローカル形式で実行されます。
- ブロックデータブロック:INSERT コマンドでデータを書き込む際、max_insert_block_size のサイズ(デフォルト 1048576 行)に基づいてデータをいくつかのブロックデータブロックに分割します。したがって、ブロックデータブロックはデータ書き込みの基本単位であり、書き込みの原子性と唯一性を持っています。
- 原子性:データ書き込み時、1 つのブロック内のデータはすべて成功するか、すべて失敗します。
- 唯一性:ブロックデータブロックを書き込む際、現在のブロックデータブロックのデータ順序、データサイズなどを基にハッシュ情報の要約を計算し、記録します。その後、書き込まれたブロックデータブロックが以前に書き込まれたブロックデータブロックと同じハッシュ要約(ブロックデータブロック内のデータ順序、データサイズ、データ行がすべて同じ)を持つ場合、そのブロックデータブロックは無視されます。
レプリカの定義形式#
レプリカを使用する利点:
- データの冗長ストレージが増加し、データ損失のリスクが低減されます。
- レプリカはマルチマスターアーキテクチャを採用しているため、各レプリカインスタンスがデータの読み書きの入り口として機能し、ノードの負荷を分散します。
ReplicatedMergeTree 原理解析#
データ構造#
コアロジックでは、ZooKeeper の機能を大量に使用して、複数の ReplicatedMergeTree レプリカインスタンス間の協調を実現します。これには、主レプリカの選挙、レプリカの状態認識、操作ログの配信、タスクキュー、ブロック ID の重複判断などが含まれます。INSERT データの書き込み、パーティションのマージ、MUTATION 操作を実行する際には、ZooKeeper との通信が関与します。ただし、通信プロセスではテーブルデータの転送は設計されておらず、データをクエリする際にも ZooKeeper にアクセスしません。