メインコンテンツまでスキップ
メインコンテンツまでスキップ

ClickHouseにおける圧縮

ClickHouseのクエリパフォーマンスの秘密の一つは圧縮です。

ディスク上のデータが少ないということは、I/Oが少なく、クエリや挿入が速くなるということを意味します。ほとんどの場合、CPUに対する任意の圧縮アルゴリズムのオーバーヘッドは、I/Oの削減によって相殺されるでしょう。したがって、ClickHouseのクエリを迅速にするための作業において、データの圧縮を改善することが最初の焦点となるべきです。

ClickHouseがデータを効率的に圧縮する理由については、こちらの記事をお勧めします。要約すると、列指向データベースであるため、値はカラム順に書き込まれます。これらの値がソートされている場合、同じ値は隣接します。圧縮アルゴリズムは、連続したデータパターンを利用します。さらに、ClickHouseにはコーデックや細かいデータ型があり、ユーザーが圧縮技術をさらに調整できるようになっています。

ClickHouseにおける圧縮には3つの主な要因が影響します:

  • 順序キー
  • データ型
  • 使用されるコーデック

これらすべてはスキーマを通じて構成されます。

圧縮を最適化するための適切なデータ型の選択

Stack Overflowのデータセットを例として使いましょう。postsテーブルの以下のスキーマの圧縮統計を比較してみます。

  • posts - 順序キーのない、型最適化されていないスキーマ。
  • posts_v3 - 各カラムに対して適切な型とビットサイズを持ち、順序キー(PostTypeId, toDate(CreationDate), CommentCount)を持つ型最適化されたスキーマ。

次のクエリを使用して、各カラムの現在の圧縮サイズと非圧縮サイズを測定できます。まず、順序キーのない初期の最適化スキーマpostsのサイズを確認しましょう。

ここでは、圧縮されたサイズと非圧縮のサイズの両方を示しています。どちらも重要です。圧縮されたサイズは、ディスクから読み取る必要があるサイズに相当します - これはクエリパフォーマンス(およびストレージコスト)のために最小化したいものです。このデータは読み取りの前に解凍する必要があります。この非圧縮のサイズは、この場合に使用されるデータ型に依存します。このサイズを最小限に抑えることで、クエリのメモリオーバーヘッドと、クエリによって処理される必要があるデータの量が削減され、キャッシュの利用が改善され、最終的なクエリ時間が短縮されます。

上記のクエリは、システムデータベース内のcolumnsテーブルに依存しています。このデータベースはClickHouseによって管理されており、クエリ性能メトリックからバックグラウンドクラスターのログまで、便利な情報の宝庫です。興味がある方には、「システムテーブルとClickHouseの内部へのウィンドウ」やその関連の記事[1][2]をお勧めします。

テーブルの合計サイズを要約するために、上記のクエリを簡略化できます:

posts_v3、すなわち最適化された型と順序キーを持つテーブルのためにこのクエリを繰り返すと、非圧縮サイズと圧縮サイズが大幅に削減されることがわかります。

完全なカラムの内訳では、データを圧縮前に順序を付け、適切な型を使用することで、BodyTitleTagsCreationDateカラムでかなりの保存が達成される様子が示されています。

適切なカラム圧縮コーデックの選択

カラム圧縮コーデックを使用することで、各カラムに対して使用されるアルゴリズム(およびその設定)を変更できます。

エンコーディングと圧縮は、データサイズを削減するという同じ目的を持ちながら、わずかに異なる方法で作用します。エンコーディングは、データ型の特性を利用して値を変換する関数に基づいてデータにマッピングを適用します。一方、圧縮は、バイトレベルでデータを圧縮するための一般的なアルゴリズムを使用します。

通常、エンコーディングは圧縮が使用される前に適用されます。異なるエンコーディングと圧縮アルゴリズムは異なる値の分布に対して効果的であるため、自分のデータを理解する必要があります。

ClickHouseは多くのコーデックと圧縮アルゴリズムをサポートしています。以下は、重要度の順にいくつかの推薦事項です:

RecommendationReasoning
ZSTDを推奨ZSTD圧縮は最高の圧縮率を提供します。ZSTD(1)は最も一般的な型のデフォルトとすべきです。圧縮率を高めるためには数値を変更することができます。圧縮コスト(挿入速度の低下)が増加するため、3以上の値では十分な利点を得られることは稀です。
日付および整数列にはDeltaDeltaベースのコーデックは、単調増加列や連続する値の小さなデルタを持つ場合に効果的です。具体的には、デルタコーデックは、導関数が小さな数を生成する場合に有効です。そうでない場合は、DoubleDeltaを試す価値があります(これにより、通常は一次導関数の値が既に非常に小さい場合にほとんど追加効果がありません)。単調増加が均一な列の場合は、さらに効果的な圧縮が実現します(例:DateTimeフィールド)。
DeltaZSTDを向上させるZSTDはデルタデータに対して効果的で、逆にデルタエンコーディングはZSTD圧縮を改善することがあります。ZSTDが存在する場合、他のコーデックはほとんどさらなる改善を提供しません。
可能であればLZ4ZSTDより優先LZ4ZSTDの間で比較可能な圧縮が得られた場合は、前者を優先してください。LZ4はより速い解凍を提供し、CPUの必要性も少ないからです。ただし、ZSTDはほとんどの場合でLZ4を大幅に上回ります。これらのコーデックの一部は、LZ4と組み合わせて使用することで、コーデックなしのZSTDと比較して似たような圧縮を提供しやすくなります。ただし、これはデータに依存するため、テストが必要です。
スパースまたは小さな範囲にT64T64はスパースデータやブロック内の範囲が小さい場合に効果的です。ランダムな数にはT64を避けましょう。
未知のパターンにはGorillaおよびT64データに未知のパターンがある場合、GorillaおよびT64を試してみる価値があります。
ゲージデータにGorillaGorillaは浮動小数点データ、特にゲージ読み取り(すなわち、ランダムなスパイク)を表すものに対して効果的です。

詳細なオプションについてはこちらを参照してください。

以下では、IdViewCount、およびAnswerCountのためにDeltaコーデックを指定し、これらが順序キーと線形相関があると仮定し、デルタエンコーディングの恩恵を受けるはずです。

これらのカラムにおける圧縮の改善は以下に示されています。

ClickHouse Cloudにおける圧縮

ClickHouse Cloudでは、デフォルトでZSTD圧縮アルゴリズム(デフォルト値は1)を利用しています。圧縮速度は、このアルゴリズムの圧縮レベル(高いほど遅い)によって異なりますが、常に速い解凍を実現できるという利点があり(約20%の変動)、並列化の能力も享受できます。これまでのテストは、このアルゴリズムがほとんどのデータ型と情報分布に対して効果的であり、最初から最適化を行わない状態でも当初の圧縮が優れていた理由です。