--- sidebar_position: 1 sidebar_label: ClickHouseはなぜ速いのか? description: "高速に設計されていました。クエリ実行のパフォーマンスは常に開発プロセスで最優先されていましたが、ユーザーフレンドリーさ、スケーラビリティ、セキュリティといった他の重要な特性も考慮され、ClickHouseが現実のプロダクションシステムとなることができました。" --- # ClickHouseはなぜ速いのか? {#why-clickhouse-is-so-fast} [データの指向性](/docs/ja/intro#row-oriented-vs-column-oriented-storage)の他にも、データベースのパフォーマンスには多くの要因が寄与します。 これから、ClickHouseが特に他の列指向データベースと比較して速い理由を詳しく説明します。 アーキテクチャの観点から見ると、データベースは少なくともストレージ層とクエリ処理層から成り立っています。ストレージ層はテーブルデータの保存、読み込み、管理を担当し、クエリ処理層はユーザークエリの実行を行います。ClickHouseは他のデータベースと比較して、挿入とSelectクエリを極めて高速に行うことを可能にする両層の革新を提供しています。 ## ストレージ層: 同時挿入は相互に分離される ClickHouseでは、各テーブルは複数の「テーブルパーツ」から構成されています。ユーザーがテーブルにデータを挿入するたびに(INSERT文)、パートが作成されます。クエリは、クエリが開始された時点に存在するすべてのテーブルパーツに対して常に実行されます。 パーツが多くたまりすぎないようにするために、ClickHouseはバックグラウンドで複数の(小さな)パーツを単一の大きなパーツに継続的に組み合わせる結合操作を実行します。 このアプローチにはいくつかの利点があります。一方で、個々の挿入は「ローカル」であり、グローバル、すなわちテーブル全体のデータ構造を更新する必要がないという点です。結果として、複数の同時挿入は相互の同期や既存のテーブルデータとの同期を必要とせず、ディスクI/Oの速度で挿入を行うことが可能です。 ## ストレージ層: 同時挿入および選択は分離される 一方で、パーツの結合はユーザーに見えないバックグラウンド操作であり、同時選択クエリには影響を与えません。実際、このアーキテクチャは挿入と選択を非常に効果的に分離しており、多くの他のデータベースで採用されています。 ## ストレージ層: 結合時の計算 他のデータベースとは異なり、ClickHouseは結合操作中に追加のデータ変換も実行できます。この例には以下が含まれます: - **Replacing merges**によって、入力パーツの行の最新バージョンのみを保持し、他のすべての行バージョンを破棄します。Replacing mergesは結合時のクリーンアップ操作と考えることができます。 - **Aggregating merges**によって、入力パーツの中間集計状態を新しい集計状態に結合します。これは、実際にはインクリメンタル集計を実装するだけです。 - **有効期限 (TTL) の結合** により、特定の時間ベースのルールに基づいて行を圧縮、移動、または削除します。 これらの変換のポイントは、ユーザークエリの実行時にかかる作業(計算)を結合時にシフトすることです。これが重要な理由は2つあります: 一方では、クエリが変換後のデータ(例:事前集計データ)を活用できる場合、ユーザークエリは劇的に速くなる可能性があります。時には1000倍以上速くなることもあります。 他方では、結合の実行時間の大部分は、入力パーツの読み込みと出力パーツの保存に費やされます。結合中にデータを変換するための追加作業は、通常、結合の実行時間にあまり影響を与えません。これらの魔法は完全に透明であり、クエリの結果に影響を与えません(パフォーマンスを除いて)。 ## ストレージ層: データのプルーニング 実際には、多くのクエリは繰り返し可能であり、変更なしまたはわずかな修正(例:異なるパラメータ値)で定期的な間隔で実行されます。同じまたは類似のクエリを何度も実行することで、インデックスを追加したり、データを再編成して頻繁なクエリがデータにより速くアクセスできるようにします。このアプローチは「データプルーニング」としても知られており、ClickHouseはこれに対して3つの技術を提供しています: 1. [主キーインデックス](https://clickhouse.com/docs/ja/optimize/sparse-primary-indexes) は、テーブルデータのソート順を定義します。適切に選ばれた主キーは、上記のクエリでのWHERE句のようなフィルタを、フルカラムスキャンではなく高速な二分探索で評価できるようにします。より技術的には、スキャンの実行時間はデータサイズに対して線形ではなく対数になります。 2. [テーブルのプロジェクション](https://clickhouse.com/docs/ja/sql-reference/statements/alter/projection) は、異なる主キーでソートされた同じデータを格納するテーブルの別の内部バージョンとして役立ちます。プロジェクションは、複数の頻繁なフィルタ条件がある場合に役立ちます。 3. [スキッピングインデックス](https://clickhouse.com/docs/ja/optimize/skipping-indexes) は、最小値や最大値、ユニークな値のセットなど、追加のデータ統計をカラムに埋め込みます。スキッピングインデックスは主キーやテーブルプロジェクションとは直交しており、カラム内のデータ分布に応じて、フィルタの評価を大幅に高速化することができます。 これら3つの技術はすべて、フルカラムの読み込み時にできるだけ多くの行をスキップすることを目的としており、データを最も速く読み取る方法は、そもそも読み取らないことです。 ## ストレージ層: データ圧縮 さらに、ClickHouseのストレージ層は生のテーブルデータをさまざまなコーデックを使用して(または必要に応じて)圧縮します。 列ストアはこのような圧縮に非常に適しており、同じ型とデータ分布の値が一緒に配置されています。 ユーザーは、[設定](https://clickhouse.com/blog/optimize-clickhouse-codecs-compression-schema) によって、カラムを様々な一般的な圧縮アルゴリズム(ZSTDなど)や特化したコーデック、例えば浮動小数点数用のGorillaとFPC、整数値用のDeltaとGCD、あるいは暗号化コーデックとしてのAESなどで圧縮することができます。 データ圧縮は、データベーステーブルのストレージサイズを削減するだけでなく、多くの場合、ローカルディスクやネットワークI/Oが低スループットに制約されているため、クエリパフォーマンスも向上させます。 ## 最新鋭のクエリ処理層 最後に、ClickHouseはベクトル化されたクエリ処理層を採用しており、最大速度と効率を実現するためにリソースを最大限に活用してクエリの実行を並列化しています。 「ベクトル化」とは、クエリプランオペレーターが中間結果の行を単一行ではなくバッチで渡すことを意味します。これにより、CPUキャッシュの利用が向上し、オペレーターがSIMD命令を適用して複数の値を一度に処理できるようになります。実際、多くのオペレーターは複数のバージョンを持っており、それぞれのSIMD命令セット世代に対応しています。ClickHouseは、動作するハードウェアの能力に基づいて、最新かつ最速のバージョンを自動的に選択します。 現代のシステムには数十のCPUコアがあります。全てのコアを活用するために、ClickHouseはクエリプランを複数のレーンに展開し、通常はコアごとに1つのレーンを作成します。各レーンはテーブルデータの非重複範囲を処理します。この方法により、データベースのパフォーマンスは利用可能なコア数に応じて「垂直に」拡張されます。 単一ノードがテーブルデータを保持するには小さすぎる場合、さらにノードを追加してクラスターを形成できます。テーブルを分割して(「シャーディング」して)ノード間に分散することができます。ClickHouseはテーブルデータを格納するすべてのノードでクエリを実行し、これにより利用可能なノード数に応じて「水平に」スケールされます。 ## 細部への徹底的なこだわり > **「ClickHouseは驚異的なシステムです - ハッシュテーブルの20バージョンがあります。通常のシステムが1つのハッシュテーブルを持つところ、ClickHouseは多様なクエリタイプ、データ構造、分布やインデックス設定にわたり、多数の専門的なコンポーネントを持つため、驚異的なパフォーマンスを実現しています。」** [Andy Pavlo, CMUのデータベース教授](https://www.youtube.com/watch?v=Vy2t_wZx4Is&t=3579s) ClickHouseを特別な存在にするのは、低レベルの最適化への徹底的なこだわりです。単に動作するデータベースを構築することも一つのことですが、さまざまなクエリタイプ、データ構造、分布、インデックス設定を横断して高速化を実現するためのエンジニアリングこそが「驚異的なシステム」の芸術が輝くところです。 **ハッシュテーブル。** ハッシュテーブルを例に取ってみましょう。ハッシュテーブルは、結合や集計で使用される中心的なデータ構造です。プログラマーとしては、以下の設計決定を検討する必要があります: * 選択するハッシュ関数、 * 衝突の解決: [オープンアドレッシング](https://en.wikipedia.org/wiki/Open_addressing) または [チェイニング](https://en.wikipedia.org/wiki/Hash_table#Separate_chaining)、 * メモリのレイアウト:キーと値のための1つの配列か、それとも別々の配列か? * フィルファクタ:いつどのようにリサイズするか?リサイズ時に値をどのように移動するか? * 削除:ハッシュテーブルはエントリーを削除することを許可すべきか? サードパーティのライブラリが提供する標準のハッシュテーブルは機能的には動作しますが、高速ではありません。優れたパフォーマンスは、徹底的なベンチマークと実験を必要とします。 [ClickHouseにおけるハッシュテーブルの実装](https://clickhouse.com/blog/hash-tables-in-clickhouse-and-zero-cost-abstractions) は、クエリとデータの特性に基づいて**30を超える事前コンパイルされたハッシュテーブルのバリエーション**から選択しています。 **アルゴリズム。** アルゴリズムについても同様です。例えば、ソートの場合を考えてみましょう: * 何をソートすべきか:数字、タプル、文字列、または構造体か? * データはRAMにあるか? * ソートは安定である必要があるか? * すべてのデータをソートする必要があるか、部分的なソートで十分か? データの特徴に依存するアルゴリズムは、その一般的な対応物よりもパフォーマンスが優れていることがよくあります。データの特性が事前に不明な場合、システムはさまざまな実装を試し、実行時に最適なものを選択できます。例えば、ClickHouseにおけるLZ4デコードの実装方法についての[記事](https://habr.com/en/company/yandex/blog/457612/)を参照してください。 ## VLDB 2024 論文 2024年8月に、私たちの最初の研究論文がVLDBに受理され、公開されました。 VLDBは非常に大きなデータベースに関する国際会議であり、データ管理の分野で最も権威のある会議の一つと広く見なされています。 数百件の提出物の中で、VLDBの採択率は通常約20%です。 ClickHouseがなぜそこまで高性能なのかについてその最も興味深いアーキテクチャおよびシステムデザインコンポーネントを簡潔に説明する[PDFの論文](https://www.vldb.org/pvldb/vol17/p3731-schulze.pdf)を読むことができます。 ClickHouseのCTOであり創設者でもあるAlexey Milovidovが、論文を発表し(スライドは[こちら](https://raw.githubusercontent.com/ClickHouse/clickhouse-presentations/master/vldb_2024/VLDB_2024_presentation.pdf)で入手可能)、時間切れとなった質疑応答を行いました。 記録されたプレゼンテーションはこちらでご覧になれます: