TL;DR
ClickHouse Cloud のコンピュートは完全にステートレスになりました。本記事では、それを可能にした最後のピースである、ローカルディスクへの依存を完全になくす Shared Catalog を備えた新しいインメモリデータベースエンジンをご紹介します。
ここに至るまでの経緯、Shared Catalog が可能にする機能、そしてデータレイクをはじめとするあらゆるデータソースに対するステートレスコンピュートの仕組みを解説します。
ステートレスコンピュートの完成
ClickHouse Cloud ではディスクが不要になりました。コンピュートノードはローカルに何も保存しません。同期も不要です。ウォームアップもありません。素早く起動し、クエリを実行して、そのまま消去される、高速で伸縮自在なステートレスコンピュートです。
これはプロトタイプではありません。今まさに稼働しています。
2022年10月に ClickHouse Cloud をローンチして以来、私たちはコンピュート層からローカルステートを最後の一つまで取り除き続けてきました。本記事では、完全なステートレスコンピュートへと至ったその歩みを段階を追って振り返ります。
最後の構成要素となったのが、データベースのメタデータをディスクから切り離す Shared Catalog です。
これにより、後ほど詳しく見ていく以下のメリットが即座にもたらされます。
-
新しい DDL 機能: アトミックな INSERT … SELECT、データベース間のリネーム、UNDROP など
-
耐障害性の高い DROP 処理: 稼働中のコンピュートノードに依存しない削除
-
ウォームアップ不要の高速プロビジョニング: 低レイテンシなスケールアウト、スケールアップ、高速な起動の基盤を確立
-
ネイティブ形式とオープン形式の双方に対応するステートレスコンピュート: Iceberg や Delta Lake などに対応
これが、現在の ClickHouse Cloud 環境を支えるアーキテクチャです。
まずは原点である「すべてをローカルに保存していたノード」から振り返りましょう。
ステージ 0: ClickHouse の原点
ClickHouse は、ストレージとコンピュートが密結合した古典的なシェアードナッシングアーキテクチャから始まりました。各サーバーは自身のローカルディスクにデータを保存してアクセスし、シャーディングによってスケールアウトを実現していました。
下図は、この構成におけるクエリ処理の流れを示しています。
アーキテクチャの各段階を説明するにあたり、完全にステートレスなクラウドネイティブの最終状態に達するまで、この図を段階的に更新していきます。

① DDL 文とカタログ検索: Atomic データベースエンジンが処理
初期の ClickHouse バージョンでは、DDL 文や SHOW TABLES、SHOW CREATE TABLE などのメタデータクエリは、バージョン 20.10 でデフォルトとなった Atomic データベースエンジン(従来の Ordinary エンジンを置き換えたもの)によって処理されていました。
Atomic は各テーブルに永続的な UUID を割り当てることで、データとテーブル名を切り離し、安全でアトミックな DDL 操作を可能にします。すべてのメタデータ(定義は .sql ファイルとして保存)をローカルディスクに保存するため、すべてのコンピュートノードが永続的なステートに縛られていました。
(図には示していませんが、クラスター環境では、CREATE、DROP、ALTER、RENAME などの DDL 文を ON CLUSTER 句を使ってブロードキャストでき、その調整は Keeper を介して行われます。この仕組みは DDL コマンドを DDL ログ/キューに追加するもので、特定のデータベースエンジンには依存しません。しかし、Keeper は完全なメタデータ状態を保持しないため、新しいノードが参加した際には過去の変更を認識できず、テーブルを再作成するための手動セットアップが必要でした。)
この仕組みは小規模なクラスターでは問題なく機能しましたが、動的なスケーリングを困難にしていました。ステートレス化を実現するには、このローカル依存を排除する必要がありました。
② データストレージ: MergeTree テーブルエンジンが処理
テーブルデータそのものに関するすべての処理(挿入、削除、更新)は、データを不変のデータパートの集合としてディスク上に整理する MergeTree ファミリーのテーブルエンジンが担当します。
ReplicatedMergeTree テーブルエンジンは、Keeper のレプリケーションログを介してデータを他のノードへ自動的にレプリケーションすることで、高可用性を実現します。
③ インメモリクエリ実行: OS ページキャッシュを使用
SQL の SELECT クエリでは、必要なデータはすべて完全にメモリ上で処理されます。まだキャッシュされていない場合はローカルディスクから読み出され、透過的に OS ページキャッシュへ配置されます。そこからデータはクエリエンジンへストリーミングされ、CPU とメモリ帯域幅を最大限に活用して高度に並列処理されます。
この構成は単一ノードや小規模クラスター環境には適していましたが、メタデータとデータがローカルディスクに密結合している点は、クラウドにおいてボトルネックとなりました。その結合を断ち切り、ステートレスコンピュートをサポートするために、ClickHouse がメタデータとデータを管理する仕組みをあらゆる層で見直す必要がありました。
その前に、これから解説するすべての根幹となる基本的な概念を整理しておきましょう。
メタデータとデータの管理方法
ClickHouse Cloud におけるステートレスコンピュートの進化を掘り下げる前に、重要な概念であるデータベースエンジンとテーブルエンジンの違いを明確にしておきましょう。
-
データベースエンジン: データベースとテーブルの定義を管理します。CREATE、DROP、RENAME などの DDL 操作を処理し、SHOW TABLES などのメタデータ検索を実行します。クラスター環境では、エンジンによって DDL の変更をノード間でレプリケーションする役割も担います。
-
テーブルエンジン: テーブルの実際のデータ(保存、インデックス作成、読み書きの方法)を管理します。これには INSERT、DELETE、UPDATE などの操作や、ローカルまたはリモートストレージへのアクセスが含まれます。
メタデータ管理とデータストレージを分離することで、各レイヤーにおける柔軟性、スケーラビリティ、専門化を一段と高められます。この分離は ClickHouse Cloud のアーキテクチャ、とりわけステートレスコンピュートへの歩みにおいて中核となる考え方です。
後述するように、コンピュートを完全にステートレスにするには、データを分離するテーブルエンジンと、メタデータを分離するデータベースエンジンという、双方での革新が必要でした。
データ、キャッシュ、メタデータの分離を通じて進められたその進化を、段階を追って見ていきましょう。
ステージ 1: SharedMergeTree によるデータの分離
ステートレスコンピュートへの最初の大きな一歩は、データストレージとコンピュートの分離でした。それは、オブジェクトストレージ向けテーブルエンジンである SharedMergeTree と、ブートストラップを簡素化するデータベースエンジンである Replicated から始まりました。
各レイヤーの役割は以下のとおりです。

① DDL 文とカタログ検索: Replicated データベースエンジンが処理
ストレージとコンピュートを分離することで、それぞれを独立してスケーリングできるようになります。ノードを自由に追加・交換できる ClickHouse Cloud の伸縮自在なコンピュートをサポートするため、バージョン 21.3 で新しいデータベースエンジン Replicated を導入しました。これは ClickHouse Cloud の登場前に実験的機能として導入され、その後クラウド向けに改良されて本番環境に対応しました。
Replicated は Atomic エンジンをベースにしており、メタデータを .sql ファイルとしてローカルに保存しつつも、CREATE、DROP、RENAME などのメタデータの変更を、ON CLUSTER 句を必要とせずに Keeper に書き込まれる DDL ログを介してノード間で自動的にレプリケーションします。
(注: Replicated エンジンは Keeper 内の DDL ログを使用してメタデータの変更をレプリケーションします。図ではわかりやすさを優先してこれを省略しています。また図示されていませんが、テーブルメタデータはディスクにキャッシュされ、アクセスされたテーブルおよびデータベースのメタデータは、高速アクセスのために OS ページキャッシュを介してメモリ内にも透過的にキャッシュされます。)
長さ制限のあるシンプルな DDL ログを用いた以前の仕組みとは異なり、Replicated データベースエンジンはデータベースごとの完全なメタデータ状態を Keeper に保存するため、新しいノードは事前の手動セットアップなしで、(事前に作成されたデータベースごとに)自身を完全にブートストラップできます。
② テーブルメタデータへのアクセス: SharedMergeTree テーブルエンジンが処理
テーブルストレージは SharedMergeTree テーブルエンジンによってコンピュートから分離され、Keeper 内のテーブルメタデータ層を介して共有オブジェクトストレージへのアクセスを調整します。このレイヤーは、各テーブルにどのデータパートが存在し、対応するファイルがオブジェクトストレージのどこにあるかを追跡します。
マイルストーン: ClickHouse Cloud のローンチ当初は ReplicatedMergeTree を採用していました。新規サービスでは以前から SharedMergeTree を使用していましたが、本記事の公開時点で既存の全サービスでも移行が完了しました。現在では、共有ストレージが全面的に基盤となっています。
③ 共有テーブルデータ: オブジェクトストレージに保存
SharedMergeTree テーブルエンジンにより、テーブルデータはローカルディスクに縛られなくなりました。データは共有オブジェクトストレージに保存され、高い耐久性を持ち、事実上無制限で、任意のコンピュートノードからアクセスできます。これにより、コンピュートノードがデータのローカルコピーをレプリケーションしたり管理したりする必要がなくなり、伸縮自在なスケーリング、耐障害性、運用の簡素化が実現します。
内部の仕組み: ClickHouse に求められる高いパフォーマンスを提供するため、私たちは基本的なオブジェクトストレージへのアクセスにとどまらない工夫を凝らしています。システムは積極的なリトライ、大きなオブジェクトの並列チャンクへの分割、非同期プリフェッチを伴うマルチスレッド読み取りを実行し、スループットと耐障害性を最大化しています。(読み取り側の最適化の詳細については、講演「Reading from object storage 100× faster」をご覧ください。)
④ クエリの高速化: ファイルシステムキャッシュでオブジェクトストレージのレイテンシを隠蔽
オブジェクトストレージは耐久性と拡張性に優れていますが、アクセスレイテンシが高いという課題があります。このレイテンシからクエリを保護するため、ClickHouse Cloud はローカルのファイルシステムキャッシュを導入しました。オブジェクトストレージからストリーミングされたデータは、将来の再利用に備えてローカルにキャッシュされます。このキャッシュは OS ページキャッシュと連携して動作し、繰り返されるクエリをメモリ速度で実行できるようにします。
⑤ クエリ実行: OS ページキャッシュを使用してメモリ内で実行
従来のシェアードナッシング構成のサーバーと同様に、ClickHouse Cloud はデータをすべてメモリ上で処理します。データは OS ページキャッシュを経由してクエリエンジンへストリーミングされ、高速かつ並列に実行されます。
ローカルファイルシステムキャッシュによって繰り返し実行されるクエリは高速化されましたが、その恩恵を受けられるのはそれらを実行したのと同じコンピュートノードだけでした。伸縮自在なコンピュート環境においては、単一のノードに紐づいたキャッシュではもはや不十分でした。
ステージ 2: 分散キャッシュによるキャッシュの分離
ホットデータをクエリエンジンの近くにキャッシュすることは、分析を高速化する最も効果的な方法の一つです。しかし、それまでの ClickHouse Cloud では、キャッシュは特定のコンピュートノードに紐づいたローカルなものでした。キャッシュを真にクラウドネイティブで伸縮自在なものにするため、さらなる一歩が必要でした。そこで構築したのが分散キャッシュです。
下図は、ホットデータをコンピュートから切り離し、すべてのノードから瞬時にアクセスできるようにする分散キャッシュが、ClickHouse Cloud アーキテクチャにどのように組み込まれているかを示しています。
各段階の理解を容易にするため、新しく追加されたコンポーネントをフルカラーで強調し、すでに説明したコンポーネントはトーンを落として表示しています。アーキテクチャの進化に合わせて、以降のステージでもこの表現を継続します。

① 共有ホットデータ: 分散キャッシュサービスにキャッシュ
分散キャッシュは、アクセスされたテーブルデータを専用のキャッシュノード群に保存する共有ネットワークサービスです。コンピュートノードは必要なデータをここから並列に取得するため、オブジェクトストレージのレイテンシを回避し、過去にキャッシュされたデータを異なるノード間であっても即座に再利用できます。
② インメモリ実行: ユーザー空間ページキャッシュで高速化
依然としてローカル RAM が最も高速なレイヤーであるため、ホットデータをメモリにキャッシュすることはクエリ速度に不可欠です。ClickHouse Cloud のコンピュートノードはキャッシュ目的でローカルディスクを使用しなくなり、OS ページキャッシュに依存できなくなったため、分散キャッシュから読み取ったデータをメモリ上にキャッシュするレイヤーとしてユーザー空間ページキャッシュを導入しました。
ホットデータのキャッシュがコンピュートから完全に切り離されたことで、残る依存関係はただ一つ、ローカルディスク上に残るデータベースメタデータだけとなりました。ステートレスアーキテクチャを完成させるには、ここにも再考が必要でした。
ステージ 3: Shared Catalog によるメタデータの分離
Replicated データベースエンジンにより、ClickHouse Cloud においてコンピュートノードを柔軟に追加・交換しやすくなりました。ノードは、データベース内に存在するテーブルの情報を自動的にブートストラップできるようになりました。しかし、それでも完全なクラウドネイティブとは言えませんでした。
-
ローカルディスクと手動オーケストレーションへの依存: メタデータはローカルファイルシステムに保存されており、データベースは各ノード上に事前に作成しておく必要がありました。
-
脆弱な障害復旧: クラッシュによって孤立したメタデータや不整合なメタデータが残る可能性がありました。
-
スケーラブルな DDL サポートの不足: DROP にはすべてのノードがオンラインである必要があり、データベースを跨ぐ RENAME やアトミックな INSERT … SELECT などの機能をクリーンに実装することが難しく、見送られていました。
そのため、SharedMergeTree テーブルエンジンのときと同様に原点に立ち返り、Shared Catalog を備えた、クラウドネイティブでステートレスな新しい Shared データベースエンジンを設計しました。
Shared データベースエンジンとカタログの仕組みを詳しく見る前に、下図でシステム全体における位置付けを確認しましょう。データベースのメタデータをローカルディスクから切り離し、真にステートレスなコンピュートノードを実現しています。

Shared データベースエンジンは、引き続き ① すべての DDL 文とカタログ検索の処理を担当しますが、ローカルファイルではなく Shared Catalog をバックエンドとして利用するようになりました。
このアーキテクチャ上の転換によって、Shared エンジンを支えるもう一つの重要な原則である「完全にステートレスでディスクレスなコンピュート」が実現します。
真のステートレス: ディスク不要、制約なし
旧来の Replicated エンジンが管理していたデータベースメタデータは、コンピュートノードがローカルディスクを必要としていた最後の理由でした。新しい Shared データベースエンジンはその依存関係を完全に排除し、純粋なインメモリエンジンとして動作します。これにより、コンピュートノードにはディスクが一切不要になり、CPU とメモリだけで稼働します。
集中管理されたメタデータ: Keeper 内でレプリケーションとバージョニングを実施
Shared データベースエンジンは、すべてのデータベースおよびテーブル定義を Keeper をバックエンドとする中央の Shared Catalog に保存します。ローカルディスクに書き込む代わりに、すべてのコンピュートノードで共有される単一のバージョニングされたグローバルステートを維持します。
各ノードは最後に適用されたバージョンのみを追跡し、起動時に最新のステートを取得します。ローカルファイルも手動セットアップも不要で、迅速かつ一貫性のあるブートストラップが可能です。
(図には示していませんが、メタデータも透過的にメモリ内にキャッシュされます。これにより、SHOW TABLES や DESCRIBE などのメタデータクエリが高速化されます。これについては、後述の「アーキテクチャの動作」のアニメーションで改めて説明します。)
この転換により、ClickHouse Cloud のコンピュートは真のステートレスとなりました。そしてそのメリットは、ディスクレスなブートストラップにとどまりません。
Shared Catalog がもたらすもの
メタデータが一元化されバージョニングされたことで、大規模環境における DDL の動作を一新できました。その結果、調整処理とオブジェクトのライフサイクルのための新しいモデルが誕生し、以下の機能が実現しました。
-
高い同時実行環境でも動作するクラウド規模の DDL
-
耐障害性の高い削除と新しい DDL 処理
-
ステートレスノードがディスク依存なしで起動することによる高速な立ち上げと起動
-
Iceberg や Delta Lake を含む、ネイティブ形式とオープン形式の双方に対応するステートレスコンピュート
ここから、それぞれの詳細を見ていきましょう。
お知らせ: ここからは詳細な解説になります
続くセクションでは、共有データベースエンジンとカタログが実際にどのように動作するのか、調整処理、整合性、そして私たちが解決しなければならなかった複雑なエッジケースに踏み込んで詳しく解説します。
内部でどのような仕組みが働いているのか興味がある方は、ぜひこのまま読み進めてください。
全体的なポイントだけを把握したい場合は、遠慮なく次のセクションへスキップしてください。
1. きめ細かな調整によるクラウド規模の DDL
これによって実現する最初の機能が、高い同時実行性やノードの頻繁な入れ替えが発生する環境でも動作するクラウド規模の DDL 調整です。
課題: 多数のノード間における高速で一貫性のあるメタデータ調整
異なるクライアントが異なるノードに対して同時に DDL コマンドを発行する可能性があるため、システムはすべての既存ノード間、および(垂直または水平にスケーリングして)新しく参加するノードに対して、整合性の取れたグローバルなメタデータ状態を維持しなければなりません。この調整には、(1)高速であること、(2)高い同時実行性をサポートすること、の双方が求められます。
単純なアプローチとしては、グローバルロックを使用する方法があります。ロックを最初に取得したノードが、競合(例: 別のノードによって削除されたばかりのデータベースをリネームするなど)を防ぐために最新のメタデータ状態を取得し、自身の DDL 変更を適用した後にロックを解放します。この方法は正確ではあるものの、すべての DDL が直列化されてしまい、最適とは言えず、(1)高速な調整と(2)高い同時実行性の両方を満たすことができません。
選択的無効化: よりスマートな解決策
グローバルロックを使用する代わりに、私たちはよりスマートなアプローチを採用しています。各 DDL コマンドの種類に応じて、競合する可能性のある他の同時実行 DDL の特定のサブセットのみを無効化し、それらのノードが最新のステートを取得して適用するまで待機させます。
Keeper が同時実行下で正確性を保証する仕組み
Shared Catalog は、線形化可能な書き込みを保証する Keeper のコンセンサスアルゴリズムを通じてすべての DDL 更新を適用することで、これを実現しています。各コンピュートノードは、DDL の変更をマルチライトトランザクションとして送信します。Keeper は、いずれかのノードのトランザクションが必ず最初に適用されることを保証します。別のノードが競合する変更を送信した場合、古いオブジェクトバージョンを参照しているため、そのマルチライトリクエストは失敗します。その後、そのノードは更新されたステートを先に取得して適用できます。これにより、同時実行性を損なうことなく整合性が保証されます。
アーキテクチャの実際の動作
以下のアニメーションは、私たちのアプローチの概要を示しています。Keeper に保存されている Shared Catalog のグローバルステートが示されています。このステートは、主に3つの Keeper ノードで構成されます。
/uuids: UUID をオブジェクトのメタデータ(例: CREATE クエリ、バージョン)にマッピング/names: オブジェクト名を UUID にマッピング(検索やリネーム用)/replicas: 各コンピュートノードが最後に適用したメタデータバージョンを追跡
グローバルな DB メタデータステートのバージョンは、/uuids ノードの Znode バージョンです。これが信頼できる唯一の情報源(Single Source of Truth)となります。
Shared Catalog の上部には3つのコンピュートノードがあり、それぞれがローカルのインメモリ DB メタデータを保持しています。これらのノードはカタログの変更をサブスクライブし、Keeper の Watch ベースの通知メカニズムを使用して最新の状態を保ちます。

① ノード 3 が DDL コマンドを受信して実行
ノード 3 は、db2 にテーブル tbl を作成する DDL コマンドを受信します。ここで、あるカラム val は DEFAULT 式を使用しており、挿入時に db1 に存在する既存の辞書 dic のルックアップによって計算されます。
これを実行するために、ノード 3 の DDL 実行スレッドは以下を行う Keeper マルチリクエストを送信します。
-
② 依存関係の検証: 必要なすべてのオブジェクト(db1、dic、db2)が存在し、期待されるバージョンであることを確認します。別のノードがそれらのいずれかをリネームまたは削除したばかりの場合、バージョンチェックは失敗し、操作はユーザーにエラーを返します。ただし、その間にノードはバックグラウンドで更新されたメタデータ状態を取得して適用するため、ユーザーが再試行した際には即座に再実行できる状態になります。
-
③ バージョンの引き上げ: 依存する各オブジェクトのバージョンをインクリメントします。これはきめ細かなロックとして機能します。DDL コマンドによってアクセスされた特定のオブジェクトのみバージョンが引き上げられます。それらの同じオブジェクトを変更しようとしている他のノードは、バージョンの不一致を検出して処理を中止します。グローバルステートの残りの部分は変更されないため、高い同時実行性が維持されます。
-
④ 変更の適用: 新しいテーブル tbl をグローバルステートに書き込み、グローバルメタデータバージョンをインクリメントして(例: 5 から 6 へ)、他のすべてのノードに通知します(後述の Keeper Watch 通知経由)。
⑤ バックグラウンドスレッドがすべてのコンピュートノード上のステートを更新
すべてのコンピュートノードは、Keeper の Watch メカニズムを介して変更を監視するバックグラウンドスレッドを実行しています。グローバルステートのバージョンが上がったこと(例: 5 から 6 へ)を検知すると、以下を実行します。
- Keeper から新しいステートを取得する。
- 変更内容をローカルメモリにマージする。
- Keeper 内の自身の
/replicasバージョンを更新して、最新状態になったことを通知する。
今回のケースでは、ノード 3 自身が変更を行ったため、変更内容のマージは行われず、バージョンマーカーの更新のみが必要です。
ノード間における DDL の可視性の保証: distributed_ddl_output_mode 設定は、変更を適用した(ステップ ④)後に DDL 実行スレッドがどのような動作をするかを制御します。例えば、クライアントに成功を返す前に、すべて(またはすべてのアクティブな)コンピュートノードがローカルメタデータを更新するまで、つまり独立したバックグラウンドスレッドによる最終更新ステップ(⑤)を完了するまで待機させることができます。最大待機時間は distributed_ddl_task_timeout 設定によって制御されます。
説明を簡単にするため、ステップ ④ の内部的な詳細の一部を省略しました。各オブジェクトは、「作成予定(intention to create)」→「作成済み(created)」といった段階のライフサイクルを経て処理され、これらは(先ほどのアニメーションで示したように)オブジェクトの stage フィールドに保存されます。次のセクションでは、なぜこの段階的なアプローチが重要なのか、そしてそれによって何が可能になるのかを説明します。
2. 段階的なメタデータライフサイクルによる信頼性の高い削除と DDL
Shared データベースエンジンは、各オブジェクトのステートを明示的に追跡する新しい内部メカニズムである段階的なオブジェクトライフサイクルを導入しています。これにより、コンピュートとメタデータが密結合していた従来のエンジンで生じていた、信頼性の高い削除、復旧、複数オブジェクト間の一貫性に関する長年の課題が解決されます。
まずは削除から見ていきましょう。従来のエンジンでは、削除はノードの稼働状態と密接に結びついていました。
-
MergeTree テーブルエンジン: データがローカルディスクに保存されていたため、DROP コマンドを実行したノードが即座にデータを削除する責任を担っていました。
-
SharedMergeTree テーブルエンジン: 共有ストレージを使用するマルチノード構成では、DROP を最後にレプリケートしたノードがデータを削除する責任を担っていました。SharedMergeTree は共有ストレージを使用するため、この遅延処理によって、どのノードからもデータが不要になった時点で確実に削除されるようになります。
これは コンピュートとコンピュートの分離(compute-compute separation) の環境では脆弱でした。あるサービスが DROP を発行した場合、そのサービス上ではテーブルが論理的に削除されますが、物理的な削除はすべてのノードがそれを認識するまで遅延されます。1 つのノード(または別のサービス)でもアイドル状態になったり停止したりすると、削除が滞り、場合によっては数週間に及ぶこともありました。
段階的な設計では、削除をライフサイクルに対応させ、独立したバックグラウンドスレッドによって非同期に処理することでこの問題を解決します。そのライフサイクルの流れを見てみましょう。
オブジェクトライフサイクルの理解
Shared Catalog 内の各オブジェクトは、明確に定義された一連のステージ(上記のアニメーション「アーキテクチャの動作」で示したように、オブジェクトの stage フィールドで追跡されます)を経て進行し、作成から削除までの安全な遷移を保証します。

① INTENTION: 作成の準備
これはすべてのオブジェクトの初期ステージです。例えば、CREATE TABLE が発行されると、DDL 実行スレッドはステージを INTENTION に設定した状態で、新しいテーブルのメタデータ(create_query ステートメントなど)を Keeper に書き込みます。この時点では、テーブルはまだ存在していません。
-
作成が成功すると、ステージは ② CREATED に更新されます。
-
失敗した場合、ステージは ④ DROP_IN_PROGRESS に設定され、メタデータはクリーンアップされます。
孤立したメタデータの解消: (ノードの障害や Keeper との切断などによって)DDL スレッドがクラッシュした場合、オブジェクトは INTENTION のまま残ります。独立したバックグラウンドのクリーンアップスレッドがこのようなケースを監視し、このステージに長時間留まっているメタデータを削除します。
これにより、Keeper 内に孤立したエントリが無期限に残る可能性があった Replicated データベースエンジンとは異なり、メタデータが残骸として放置されることがなくなります。
② CREATED: オブジェクトの稼働
これは、正常に作成された後のメタデータオブジェクトの通常の稼働中ステージです。
-
DETACH コマンドはオブジェクトを ⑤ DETACHED ステージに移動し、ATTACH コマンドはそれを CREATED に戻します。
-
DROP コマンドはオブジェクトを ③ DROP_SCHEDULED に遷移させ、そこでは非同期に削除が処理されます。また、UNDROP コマンドはオブジェクトを CREATED に戻します(後述)。
③ DROP_SCHEDULED: 論理削除、猶予期間中
オブジェクト(テーブルなど)はコンピュートノードから不可視になり、論理的に DROP された状態になります。
-
設定可能なタイムアウト(例: 8 時間)の経過後、どのコンピュートノードからも独立して動作するバックグラウンドの削除スレッドが、オブジェクトのデータを物理的に削除します。
-
この遅延削除により、猶予期間中はメタデータとデータの両方がまだ存在しているため、テーブルを安全に UNDROP できます。UNDROP が成功すると、オブジェクトは ② CREATED ステージに戻ります。
完全に分離された削除: システムは削除処理を完了するために特定のノードに依存しなくなり、従来のエンジンに見られたノード稼働状況に起因する問題を回避できます。
④ DROP_IN_PROGRESS: 最終削除の実行中
これは後戻りできないポイントであり、オブジェクトを復旧することはできなくなります。バックグラウンドの削除スレッドがオブジェクトをアクティブに削除中であることを示します。まずオブジェクトストレージからデータを削除し、次に Keeper からメタデータを削除します。
ライフサイクルステージが可能にする新しい DDL 操作
この段階的なモデルにより、従来は脆弱であったり、すっきりと実装することが難しかった DDL 機能の安全で信頼性の高い実装が可能になります。
UNDROP:
DROP_SCHEDULED の状態にあるテーブルは、メタデータとデータの両方がまだ存在している間、安全に復旧できます。
アトミックな CREATE TABLE AS SELECT (CTAS):
以前は、SELECT が失敗した場合に CTAS が中途半端に作成されたテーブルを残してしまうことがありました。現在は以下のようになります。
-
テーブルを INTENTION ステージで開始する
-
データをテーブルに書き込む
-
成功した場合は CREATED に昇格させる
-
失敗した場合は DROP_IN_PROGRESS に移動して破棄する
結果として、オール・オア・ナッシングのセマンティクスが実現され、手動でのクリーンアップは不要になります。
データベース間の RENAME:
従来のエンジンはメタデータをデータベースごとのログに保存していたため、データベース間の操作を連携させることが困難でした。Shared Catalog は、単一ソースのメタデータと Keeper を介した複数書き込みトランザクションによってこれを解決します。
これらはすべて同じ保証の恩恵を受けています。ライフサイクルを意識したメタデータにより、ワークロードがどれほど分散していてもクリーンな遷移が保証されます。
しかし、ライフサイクルの連携は、Shared Catalog が実現する利点のひとつにすぎません。また、即時プロビジョニングに対する最後のボトルネックも解消され、コンピュートノードをこれまで以上に高速に起動できるようになります。
3. 高速性を追求したプロビジョニング(さらなる高速化へ)
起動レイテンシの削減は ClickHouse Cloud の初日からの目標であり、今回のリリースはそれを現実のものとする基盤を築きます。Shared Catalog の導入により、コンピュートノードはローカルディスクや手動のオーケストレーションに依存しなくなりました。ディスクに同期するものが何もないため、どこからでもクリーンに起動できます。
4. ネイティブ形式とオープン形式の双方に対応するステートレスコンピュート
Shared Catalog は、内部メタデータだけを支えているわけではありません。DataLakeCatalog などの 統合データベースエンジン の基盤にもなっており、ステートレスなコンピュートノードが Hive、AWS Glue、Unity、Polaris といった外部カタログへシームレスに接続できるようにします。これらの統合により、ClickHouse は Iceberg や Delta Lake などの外部オープンテーブル形式を 直接 一覧表示してクエリを実行できるようになります。
ネイティブテーブルと同様に、同じパフォーマンスレイヤーがそのまま適用されます。
① Shared catalog が外部テーブルへのシームレスなメタデータアクセスを実現します。
② 分散キャッシュ がリモートオブジェクトストレージ内のコールドデータへのアクセスを高速化します。
③ ユーザー空間ページキャッシュ が ④ 高度に並列化されたインメモリクエリ実行 を保証します。

ネイティブテーブル向けに構築した同じパフォーマンスレイヤーが、データレイクのクエリも高速化し、ClickHouse Cloud のコンピュートノードはあらゆるデータに対して即座にクエリを実行できる状態になっています。独自の MergeTree テーブルであっても、外部の Iceberg や Delta Lake テーブルであっても、エンジンと実行パスは変わりません。
今後の展望
Shared Catalog は最後の構成要素でした。これによってステートレスアーキテクチャが完成しただけでなく、幅広いアーキテクチャ上の改善が実現しました。
-
俊敏性: ウォームアップ不要のプロビジョニングと、どこからでも起動できるステートレスなコンピュートノードによるディスクレスのスケールアウトおよびスケールアップ。
-
耐障害性: ノードがダウンした場合でもクリーンに完了する DROP 操作。
-
正確性: INSERT … SELECT、UNDROP、データベース間の RENAME などのアトミックな DDL が、確実に成功するかクリーンにロールバックされることを保証。
-
オープン性: ネイティブテーブルと Iceberg や Delta Lake などのオープン形式の双方にまたがるステートレスコンピュート。
ステートレスコンピュートが、ここに完成しました。
そして、私たちの取り組みはまだ始まったばかりです。
クエリを高速化するために即座に立ち上がるステートレスワーカーの群れを想像してみてください。あるいは、クラスターやマシンについて考える必要すらない真のサーバーレス体験を思い浮かべてみてください。クエリを送信するだけで、高速に結果を取得できます。

それが私たちが目指している方向です。Shared Catalog の導入により、次のチャプターに進む準備が整った真にステートレスなエンジンが構築されました。 今後の展開にもご期待ください。
今すぐ ClickHouse Cloud を始めて、$300 のクレジットを受け取りましょう。30 日間の無料トライアル終了後は、従量課金プランに移行できます。ボリュームベースの割引について詳しくは お問い合わせ ください。詳細は 料金ページ をご覧ください。



