Elastic Stack vs ClickStack
- UI and Alerting: データのクエリ、ダッシュボードの構築、アラートの管理を行うためのツール。
- Storage and Query Engine: オブザーバビリティデータの保存と分析クエリの実行を担うバックエンドシステム。
- Data Collection and ETL: テレメトリーデータを収集し、インジェスト前に処理するエージェントとパイプライン。
| Role | Elastic Stack | ClickStack | Comments |
|---|---|---|---|
| UI & Alerting | Kibana — ダッシュボード、検索、アラート | ClickStack UI (HyperDX) — リアルタイム UI、検索、アラート | どちらも、可視化やアラート管理を含む主要なユーザーインターフェイスとして機能します。ClickStack UI はオブザーバビリティ向けに特化して設計されており、OpenTelemetry のセマンティクスと密接に結び付いています。 |
| Storage & Query Engine | Elasticsearch — 転置索引を備えた JSON ドキュメントストア | ClickHouse — ベクトル化エンジンを備えたカラム指向データベース | Elasticsearch は検索に最適化された転置索引を使用し、ClickHouse は構造化データおよび半構造化データに対する高速分析のために列指向ストレージと SQL を使用します。 |
| Data Collection | Elastic Agent, Beats (e.g. Filebeat, Metricbeat) | OpenTelemetry Collector (edge + gateway) | Elastic は、Fleet で管理されるカスタムシッパーと統合エージェントをサポートしています。ClickStack は OpenTelemetry を基盤としており、ベンダーニュートラルなデータ収集と処理を可能にします。 |
| Instrumentation SDKs | Elastic APM agents (proprietary) | OpenTelemetry SDKs (distributed by ClickStack) | Elastic の SDK は Elastic Stack に密接に結び付いています。ClickStack は、主要言語における logs、メトリクス、traces 向けに OpenTelemetry SDKs を基盤としています。 |
| ETL / Data Processing | Logstash, ingest pipelines | OpenTelemetry Collector + ClickHouse materialized view | Elastic はデータ変換に ingest pipelines と Logstash を使用します。ClickStack は、materialized view と OTel collector processors によってコンピュートを insert time に移し、データを効率的かつ段階的に変換します。 |
| Architecture Philosophy | 垂直統合型で proprietary なエージェントとフォーマット | オープン標準ベースで疎結合なコンポーネント | Elastic は緊密に統合されたエコシステムを構築します。ClickStack は、柔軟性とコスト効率を高めるため、モジュール性と標準 (OpenTelemetry、SQL、object storage) を重視しています。 |
Elasticsearch と ClickHouse
中核となる構造概念
| Elasticsearch | ClickHouse / SQL | 説明 |
|---|---|---|
| Field | Column | 特定の型の1つ以上の値を保持する、データの基本単位です。Elasticsearch のフィールドには、プリミティブだけでなく配列やオブジェクトも格納できます。1つのフィールドに設定できる型は1つだけです。ClickHouse も配列やオブジェクト (Tuples, Maps, Nested) をサポートしており、さらに Variant や Dynamic のような動的型により、1つのカラムに複数の型を持たせることもできます。 |
| Document | Row | フィールド (カラム) の集合です。Elasticsearch のドキュメントは、データに応じて新しいフィールドが動的に追加されるため、デフォルトでより柔軟です (型はデータから推論されます) 。一方、ClickHouse の行はデフォルトではスキーマに従うため、ユーザーは行のすべてのカラム、またはその一部を挿入する必要があります。ClickHouse の JSON 型では、挿入されたデータに基づいて同様の半構造化 Dynamic カラムを動的に作成できます。 |
| Index | Table | クエリ実行とストレージの単位です。どちらのシステムでも、クエリは行やドキュメントを格納するインデックスまたはテーブルに対して実行されます。 |
| Implicit | Schema (SQL) | SQL のスキーマは、テーブルをネームスペースにまとめる仕組みで、多くの場合アクセス制御に使われます。Elasticsearch と ClickHouse にはスキーマはありませんが、どちらもロールと RBAC によって行レベルおよびテーブルレベルのセキュリティをサポートしています。 |
| Cluster | Cluster / Database | Elasticsearch のクラスターは、1つ以上のインデックスを管理するランタイムインスタンスです。ClickHouse では、database が論理ネームスペース内でテーブルを整理し、Elasticsearch におけるクラスターと同様の論理的なグループ化を提供します。ClickHouse クラスターは、Elasticsearch と同様に分散ノードの集合ですが、データそのものとは切り離されており、独立しています。 |
データモデリングと柔軟性
Dynamic、Variant、JSON 型によって柔軟性も備えています。これらにより、半構造化データを取り込むことができ、Elasticsearch と同様に動的なカラム作成や型推論が可能になります。同様に、Map 型では任意のキー・バリューのペアを格納できますが、キーと値にはそれぞれ単一の型が適用されます。
型の柔軟性に対する ClickHouse のアプローチは、より明示的で制御しやすいものです。型の競合によってインジェストエラーが発生することがある Elasticsearch とは異なり、ClickHouse では Variant カラムに複数の型が混在するデータを格納でき、JSON 型を使ってスキーマの進化にも対応できます。
JSON を使用しない場合、スキーマは静的に定義されます。行に値が指定されていない場合は、Nullable として定義するか (ClickStack では使用されません) 、その型のデフォルト値 (たとえば String なら空文字列) が使用されます。
インジェストと変換
enrich、rename、grok) を含む ingest pipeline を使用します。ClickHouse では、同様の機能を インクリメンタルmaterialized view で実現できます。これにより、入力データを フィルタリングや変換 したり、エンリッチ したりして、その結果をターゲットテーブルに insert できます。materialized view の出力のみを保存したい場合は、Null table engine にデータを insert することもできます。つまり、保持されるのは materialized view の結果だけで、元のデータは破棄されるため、ストレージ容量を節約できます。
エンリッチについては、Elasticsearch は、ドキュメントにコンテキストを追加するための専用の enrich processor をサポートしています。ClickHouse では、dictionaries を クエリ時 と インジェスト時 の両方で使用して、行をエンリッチできます。たとえば、IP を場所に対応付ける ことや、INSERT 時に ユーザーエージェントのルックアップ を適用することができます。
クエリ言語
ES|QL 経由で利用できるのは left outer joins のみです。ClickHouse は、すべての join タイプ、window functions、サブクエリ (相関サブクエリを含む) 、CTE を含む 完全な SQL 構文 をサポートしています。これは、オブザーバビリティシグナルとビジネスデータまたはインフラストラクチャデータを相関付ける必要がある場合に大きな利点となります。
ClickStack では、ClickHouse バックエンド経由で完全な SQL をサポートするとともに、移行しやすいように Lucene 互換の検索インターフェイスを UI で提供しています。この構文は、Elastic query string の構文に近いものです。この構文の正確な比較については、“Searching in ClickStack and Elastic” を参照してください。
ファイルフォーマットとインターフェイス
インデックスとストレージ
Elasticsearch における挿入処理Ⓐ 新しく挿入されたドキュメントは、まずメモリ内のインデックスバッファに入り、このバッファはデフォルトで 1 秒ごとにフラッシュされます。フラッシュされたドキュメントの送信先分片はルーティング式によって決定され、その分片に対して新しいセグメントがディスクに書き込まれます。クエリ効率を向上させ、削除または更新されたドキュメントを物理的に削除できるようにするため、セグメントはバックグラウンドで継続的により大きなセグメントへマージされ、最大サイズの 5 GB に達するまで統合されます。ただし、より大きなセグメントへのマージを強制することも可能です。
_source に保存する点です (LZ4、Deflate、または ZSTD で圧縮) 。一方、ClickHouse は別個のドキュメント表現を保存しません。データはクエリ時にカラムから再構築されるため、ストレージ容量を節約できます。Elasticsearch でも Synthetic _source を使えば同様のことが可能ですが、いくつかの制約があります。また、_source を無効化することにも、ClickHouse には当てはまらない影響があります。
Elasticsearch では、index mappings (ClickHouse におけるテーブルスキーマに相当) が、フィールドの型と、その永続化およびクエリに使用されるデータ構造を制御します。
これに対して ClickHouse は カラム指向 です。つまり、各カラムは独立して保存されますが、常にテーブルの主キー / ordering key に従ってソートされます。この並び順により、スパースプライマリ索引 が機能し、ClickHouse はクエリ実行時にデータを効率よくスキップできます。クエリが主キーフィールドでフィルタする場合、ClickHouse は各カラムの関連する部分だけを読み取るため、ディスク I/O を大幅に削減し、すべてのカラムに完全な索引がなくても高い性能を実現できます。
ClickHouse はさらに、選択したカラムに対して索引データを事前計算することでフィルタリングを高速化する スキップ索引 もサポートしています。これらは明示的に定義する必要がありますが、性能を大幅に向上させることがあります。さらに、ClickHouse ではカラムごとに 圧縮コーデック と圧縮アルゴリズムを指定できます。これは Elasticsearch ではサポートされていません (Elasticsearch の 圧縮 は _source の JSON 保存にのみ適用されます) 。
ClickHouse もシャーディングをサポートしていますが、そのモデルは 垂直スケーリング を優先するよう設計されています。単一の分片で 数兆行 を格納でき、メモリ、CPU、ディスクの余力がある限り、高い性能を維持できます。Elasticsearch とは異なり、分片ごとの 厳密な行数上限 はありません。ClickHouse の分片は論理的なもので、実質的には個別のテーブルであり、データセットが単一ノードの容量を超えない限り、パーティション化は不要です。これは通常、ディスク容量の制約によって発生し、その場合にのみ、水平スケールアウトが必要になった時点でシャーディング ① が導入されるため、複雑さとオーバーヘッドを抑えられます。この場合、Elasticsearch と同様に、1 つの分片がデータの一部を保持します。単一の分片内のデータは、③ 複数のデータ構造を含む ② 不変のデータパーツの集合として構成されます。
ClickHouse の分片内の処理は 完全に並列化 されており、ノード間でのデータ移動に伴うネットワークコストを避けるため、できるだけ垂直スケーリングすることが推奨されます。
ClickHouse における挿入処理ClickHouse の挿入は、デフォルトでは同期的 です。つまり、書き込みはコミット後にのみ確認応答されます。ただし、Elastic のようなバッファリングやバッチ処理に合わせて、非同期挿入 に設定することもできます。asynchronous data inserts を使用する場合、Ⓐ 新たに挿入された行はまず Ⓑ メモリ内の挿入バッファに入り、これはデフォルトで 200 ミリ秒ごとにフラッシュされます。複数の分片を使用する場合は、新たに挿入された行を対象の分片へルーティングするために 分散テーブル が使用されます。新しいパーツがその分片のディスク上に書き込まれます。
分散とレプリケーション
SELECT クエリをすべての分片に転送して結果をマージします。INSERT 操作では、データを各分片に均等にルーティングして負荷を分散します。ClickHouse のレプリケーションは非常に柔軟で、任意のレプリカ (分片のコピー) が書き込みを受け付けられ、すべての変更は他のレプリカへ非同期に同期されます。このアーキテクチャにより、障害時やメンテナンス中でもクエリ提供を中断せずに済み、再同期も自動で処理されるため、データレイヤーで primary-secondary を強制する必要がありません。
ClickHouse CloudClickHouse Cloud では、アーキテクチャとして shared-nothing のコンピュートモデルが採用されており、単一の 分片がオブジェクトストレージに支えられます。これにより、従来のレプリカベースの高可用性を置き換え、1 つの分片を 複数のノードから同時に読み書きできる ようになります。ストレージとコンピュートを分離することで、明示的なレプリカ管理なしに弾力的なスケーリングが可能になります。
- Elastic: 分片は JVM メモリに結び付いた物理的な Lucene 構造です。分片を過剰に増やすと性能低下を招きます。レプリケーションは同期的で、master node によって調整されます。
- ClickHouse: 分片は論理的で、垂直方向にスケールでき、ローカル実行の効率も非常に高くなっています。レプリケーションは非同期ですが (逐次的にすることも可能) 、協調の仕組みは軽量です。
重複排除とルーティング
_id に基づいてドキュメントの重複を排除し、それに応じて分片にルーティングします。ClickHouse はデフォルトの行識別子を保存しませんが、挿入時の重複排除をサポートしているため、失敗した insert を安全に再試行できます。より細かく制御したい場合は、ReplacingMergeTree やその他のテーブルエンジンを使って、特定のカラムに基づく重複排除を行えます。
Elasticsearch のインデックスルーティングでは、特定のドキュメントが常に特定の分片にルーティングされるようになっています。ClickHouse では、分片キーを定義するか、Distributed テーブルを使用することで、同様のデータ局所性を実現できます。
集計と実行モデル
SELECT count(*) FROM ... GROUP BY ... SQLクエリに相当する Elasticsearch の機能は、terms aggregation で、これは Elasticsearch の bucket aggregation の 1 つです。
ClickHouse の count(*) を使った GROUP BY と Elasticsearch の terms aggregation は、機能面では概ね同等ですが、実装、パフォーマンス、結果の品質には大きな違いがあります。
Elasticsearch におけるこの集計は、クエリ対象のデータが複数の分片にまたがる場合、“top-N” クエリの結果を推定します (たとえば、件数上位 10 件のホスト) 。この推定によって速度は向上しますが、正確性が損なわれる可能性があります。この誤差は、doc_count_error_upper_bound を確認し、shard_size パラメータを増やすことで抑えられますが、その代償としてメモリ使用量の増加とクエリパフォーマンスの低下が生じます。
Elasticsearch では、すべてのバケット集約に対して size 設定 も必要です。上限を明示的に設定せずに、すべての一意なグループを返す方法はありません。高カーディナリティの集計では、max_buckets 制限 に達するおそれがあるほか、composite 集約 を使ったページネーションが必要になる場合がありますが、これはしばしば複雑で非効率です。
これに対して ClickHouse は、追加の設定なしで正確な集計を実行します。count(*) のような関数は設定調整を必要とせずに正確な結果を返すため、クエリの挙動がよりシンプルで予測しやすくなります。
ClickHouse には size の制限がありません。大規模なデータセットに対して、上限なしの group-by クエリを実行できます。メモリのしきい値を超えた場合、ClickHouse はディスクにスピルできます。主キーのプレフィックスでグループ化する集計はとくに効率的で、最小限のメモリ消費で実行できることがよくあります。
実行モデル
- SIMD ベクトル化: 列指向データに対する操作では CPU SIMD 命令 (例: AVX512) を使用し、値をバッチ処理できます。
- クラスター レベルの並列化: 分散構成では、各ノードがローカルでクエリ処理を実行します。部分集計状態 はクエリを開始したノードにストリーミングされ、そこでマージされます。クエリの
GROUP BYキーが分片キーと一致していれば、マージは最小限に抑えるか、完全に回避することも可能です。
このモデルにより、コアやノードをまたいで効率的にスケールできるため、ClickHouse は大規模な分析処理に適しています。部分集計状態 を使用することで、異なるスレッドやノードからの中間結果を精度を損なうことなくマージできます。 一方 Elasticsearch では、多くの集計で、利用可能な CPU コア数にかかわらず、通常は 1 分片あたり 1 スレッドが割り当てられます。これらのスレッドは分片ローカルな top-N の結果を返し、それがコーディネーティングノードでマージされます。このアプローチでは、システムリソースを十分に活用できないことがあり、特に頻出する項目が複数の分片に分散している場合、グローバル集計で不正確さが生じる可能性があります。精度は
shard_size パラメータを増やすことで向上できますが、その代償としてメモリ使用量とクエリレイテンシが増加します。
要するに、ClickHouse はより細粒度の並列化と、ハードウェアリソースをより細かく制御できる実行方式によって集計やクエリを実行するのに対し、Elasticsearch はより制約の多い分片ベースの実行に依存しています。
各技術における集計の仕組みの詳細については、ブログ記事 “ClickHouse vs. Elasticsearch: Count 集計の仕組み” をご覧になることをお勧めします。
データ管理
インデックス ライフサイクル管理とネイティブ TTL の比較
ストレージ階層と hot-warm アーキテクチャ
MergeTree のようなネイティブのテーブルエンジンによって 階層型ストレージ をサポートしており、カスタムルールに基づいて古いデータを異なる ボリューム (例: SSD から HDD、さらにオブジェクトストレージへ) 間で自動的に移動できます。これにより、Elastic の hot-warm-cold アプローチを再現できますが、複数のノードロールやクラスターを管理する複雑さはありません。
ClickHouse CloudClickHouse Cloud では、これはさらにシームレスです。すべてのデータは オブジェクトストレージ (例: S3) に保存され、コンピュートは分離されています。データはクエリされるまでオブジェクトストレージに保持され、クエリ時にローカル (または分散キャッシュ) に取得されてキャッシュされます。これにより、Elastic の frozen tier と同等のコスト特性を実現しつつ、より優れた性能特性を得られます。このアプローチでは、データをストレージ階層間で移動する必要がないため、hot-warm アーキテクチャは不要になります。
ロールアップとインクリメンタル集計の比較
blue document (11、12、13) を検出します。そのため、source index は既存のすべての blue document を対象にフィルタリングされ、composite 集約 (結果の pagination を利用するため) によって集計値が再計算されます (そして宛先 index は、以前の集計値を含む document を置き換える document で更新されます) 。同様に ② と ③ でも、変更を確認し、同じ blue bucket に属する既存のすべての document から集計値を再計算することで、新しい checkpoint が処理されます。
一方、ClickHouse は根本的に異なるアプローチを取ります。定期的にデータを再集計するのではなく、ClickHouse は インクリメンタルmaterialized view をサポートしており、データを 挿入時 に変換・集計します。新しいデータがソーステーブルに書き込まれると、materialized view は新たに 挿入 された block に対してのみ、あらかじめ定義された SQL の集計クエリを実行し、その集計結果をターゲットテーブルに書き込みます。
このモデルは、ClickHouse が partial aggregate states をサポートしていることで実現されています。これは、保存して後からマージできる集計関数の中間表現です。これにより、高速にクエリでき、更新コストも低い部分集計結果を維持できます。データの到着時に集計が行われるため、高コストな定期 job を実行したり、古いデータを再度要約したりする必要はありません。
次に、インクリメンタルmaterialized view の仕組みを抽象的に示します (なお、事前に集計値を計算したい同じグループに属するすべての行を青色で表しています) :
上の図では、materialized view のソーステーブルには、同じグループに属するいくつかの blue 行 (1〜10) を格納した data part がすでに含まれています。このグループについては、view のターゲットテーブルにも、blue グループの 部分集計状態 を格納した data part がすでに存在します。新しい行を伴う ① ② ③ のソーステーブルへの insert が行われると、各 insert ごとに対応するソーステーブルの data part が作成され、同時に、新たに挿入された行の各 block に対してのみ 部分集計状態 が計算され、data part の形で materialized view のターゲットテーブルに挿入されます。④ バックグラウンドでの part merge の際に 部分集計状態 がマージされ、インクリメンタルなデータ集計が実現されます。
なお、aggregate functions は、その aggregate function combinators との組み合わせも含めて、すべて (90 種類以上) が 部分集計状態 をサポートしています。
Elasticsearch と ClickHouse におけるインクリメンタル集計の、より具体的な比較については、この example を参照してください。
ClickHouse のアプローチには、次のような利点があります:
- 常に最新の集計: materialized view は常にソーステーブルと同期されています。
- バックグラウンドジョブ不要: 集計はクエリ時ではなく挿入時に行われます。
- 優れたリアルタイム性能: 最新の集計結果が即座に必要なオブザーバビリティのワークロードやリアルタイム分析に最適です。
- 組み合わせ可能: materialized view は、より複雑なクエリ高速化戦略に向けて、他のビューやテーブルと重ねたり結合したりできます。
- 異なる有効期限 (TTL): materialized view のソーステーブルとターゲットテーブルには、それぞれ異なる TTL 設定を適用できます。
レイクハウスのサポート
- データカタログインテグレーション: ClickHouse は AWS Glue などのデータカタログとのインテグレーションをサポートしており、オブジェクトストレージ内のテーブルを自動的に検出してアクセスできます。
- オブジェクトストレージのサポート: データを移動させることなく、S3、GCS、Azure Blob Storage 上のデータをクエリできるネイティブサポート。
- クエリフェデレーション: external dictionaries や table functions を使用して、レイクハウステーブル、従来型データベース、ClickHouse テーブルなど、複数のログソースにまたがるデータを相関付ける機能。
- 増分ロード: S3Queue や ClickPipes などの機能を使用して、レイクハウステーブルからローカルの MergeTree テーブルへの継続的なロードをサポートします。
- パフォーマンス最適化: パフォーマンス向上のため、cluster functions を使用してレイクハウスデータに対する分散クエリ実行を行います。