BigQueryからClickHouse Cloudへの移行
なぜ ClickHouse Cloud を BigQuery より使うべきか?
TLDR: ClickHouse は現代のデータ分析において BigQuery よりも高速で、コストが低く、より強力です。

BigQuery から ClickHouse Cloud へのデータのロード
データセット
BigQuery から ClickHouse Cloud への典型的な移行を示すための例として、Stack Overflow のデータセットを使用します。このデータセットには、2008 年から 2024 年 4 月まで Stack Overflow で発生したすべての post
、vote
、user
、comment
、badge
が含まれています。このデータの BigQuery スキーマは以下に示されています。

このデータセットを BigQuery インスタンスにロードして移行手順をテストしたいユーザー向けに、GCS バケットに Parquet 形式のデータを提供しており、BigQuery でのテーブルの作成とロードに必要な DDL コマンドは こちら で入手できます。
データの移行
BigQuery と ClickHouse Cloud の間でデータを移行する方法は、主に2つのワークロードタイプに分類されます。
- 初期バルクロードと定期的な更新 - 初期データセットを移行し、例えば日次で定期的な更新を行う必要があります。ここでの更新は、変更された行を再送信することによって処理されます - 比較に使用できるカラム(例えば日付)で特定されます。削除はデータセットの完全な定期的再ロードによって処理されます。
- リアルタイム複製または CDC - 初期データセットを移行した後、このデータセットの変更を ClickHouse に近リアルタイムで反映させる必要があります。数秒の遅延は許容されます。これは実質的に Change Data Capture (CDC) プロセス であり、BigQuery のテーブルは ClickHouse と同期する必要があります。つまり、BigQuery テーブルの挿入、更新、削除は ClickHouse 内の同等のテーブルに適用される必要があります。
Google Cloud Storage (GCS) 経由のバルクロード
BigQuery はデータを Google のオブジェクトストア (GCS) にエクスポートすることをサポートしています。サンプルデータセットの場合:
-
7 テーブルを GCS にエクスポートします。そのためのコマンドは こちら で入手できます。
-
データを ClickHouse Cloud にインポートします。これには gcs テーブル関数 を使用できます。DDL およびインポートクエリは こちら で入手できます。ClickHouse Cloud のインスタンスが複数のコンピュートノードで構成されているため、
gcs
テーブル関数ではなく、s3Cluster テーブル関数 を使用しています。この関数は gcs バケットでも動作し、ClickHouse Cloud サービスのすべてのノードを利用して データを並列にロードします。

このアプローチにはいくつかの利点があります。
- BigQuery のエクスポート機能は、データのサブセットをエクスポートするためのフィルタをサポートしています。
- BigQuery は Parquet、Avro、JSON、CSV フォーマットやいくつかの 圧縮タイプ にエクスポートすることをサポートしており、すべて ClickHouse によってサポートされています。
- GCS は オブジェクトライフサイクル管理 をサポートしており、ClickHouse にエクスポートしてインポートされたデータを指定された期間後に削除できます。
- Google は GCS に最大 50TB を無料でエクスポートできます。ユーザーは GCS ストレージの料金のみを支払います。
- エクスポートは複数のファイルを自動的に生成し、それぞれを最大 1GB のテーブルデータに制限します。これは ClickHouse にとって有益であり、インポートを並列化することを可能にします。
以下の例を試す前に、ユーザーは エクスポートに必要な権限 と ローカリティの推奨事項 を確認して、エクスポートとインポートのパフォーマンスを最大化することをお勧めします。
スケジュールされたクエリ経由のリアルタイム複製または CDC
Change Data Capture (CDC) は、テーブルを2つのデータベース間で同期させるプロセスです。更新や削除が近リアルタイムで処理される必要がある場合、これはかなり複雑です。1つのアプローチは、BigQuery の スケジュールされたクエリ機能を使用して定期的なエクスポートを単純にスケジュールすることです。ClickHouse に挿入されるデータに若干の遅延を受け入れられる場合、このアプローチは実装と維持が容易です。例は このブログ記事 に記載されています。
スキーマの設計
Stack Overflow のデータセットには、いくつかの関連テーブルが含まれています。まず主テーブルの移行に焦点を当てることをお勧めします。これは必ずしも最も大きなテーブルである必要はなく、むしろ最も分析クエリを受けることが期待されるテーブルです。これにより、主要な ClickHouse の概念に慣れることができます。このテーブルは、追加のテーブルが追加されるにつれて、ClickHouse の機能をフルに活用し、最適なパフォーマンスを得るために再構成が必要な場合があります。このモデリングプロセスについては データモデリングドキュメント で探ります。
この原則に従って、主な posts
テーブルに焦点を当てます。この BigQuery スキーマは以下に示されています。
型の最適化
ここで説明されたプロセスを適用すると、以下のスキーマが得られます。
このテーブルにデータを読み込むために、エクスポートされたデータを gcs から読み取る単純な INSERT INTO SELECT
を使ってこのテーブルにデータを挿入することができます。なお、ClickHouse Cloud では、複数のノードをまたいでロードを並列化するために、gcs 互換の s3Cluster
テーブル関数 も使用できます。
新しいスキーマには null を保持しません。上記の挿入は、これらをそれぞれの型のデフォルト値 - 整数の場合は 0、文字列の場合は空の値に暗黙的に変換します。ClickHouse は、任意の数値をターゲットの精度に自動的に変換します。
ClickHouse の主キーの違いは?
こちらで説明されているように、BigQuery と同様に、ClickHouse ではテーブルの主キー列の値の一意性が強制されません。
BigQuery のクラスタリングと同様に、ClickHouse テーブルのデータは主キー列でディスクに順序付けて保存されます。このソート順は、クエリオプティマイザーによって利用され、再ソートを防ぎ、結合のメモリ使用量を最小限に抑え、リミット句の短絡を可能にします。 BigQuery と比較して、ClickHouse は主キー列の値に基づいて (スパース)主インデックス を自動的に作成します。このインデックスは、主キー列にフィルタを含むすべてのクエリを高速化するために使用されます。具体的には:
- メモリとディスクの効率は、ClickHouse がしばしば使用されるスケールにとって非常に重要です。データは、パーツと呼ばれるチャンクで ClickHouse テーブルに書き込まれ、バックグラウンドでパーツをマージするためのルールが適用されます。ClickHouse では、各パートには独自の主インデックスがあります。パーツがマージされると、マージされた部分の主インデックスもマージされます。ただし、これらのインデックスは各行ごとに構築されるわけではありません。代わりに、パートの主インデックスには、行のグループごとに1つのインデックスエントリがあります - この技術はスパースインデクシングと呼ばれます。
- スパースインデクシングが可能であるのは、ClickHouse がパートの行を、指定されたキーでディスクに順序付けて保存するためです。単一行を直接特定する(B-Tree ベースのインデックスのように)代わりに、スパース主インデックスは、クエリにマッチする可能性のある行のグループを迅速に識別することを可能にします(インデックスエントリに対する二分探索を介して)。特定された潜在的に一致する行のグループは、並行して ClickHouse エンジンにストリーミングされ、一致を見つけるために使用されます。このインデックス設計により、主インデックスは小さく(メインメモリに完全に収まる)、データ分析のユースケースで典型的な範囲クエリの実行時間を大幅に短縮します。詳細については、この詳細ガイドをお勧めします。

ClickHouse で選択された主キーは、インデックスだけでなく、ディスクに書き込まれるデータの順序も決定します。これにより、圧縮レベルに大きく影響を与え、結果的にクエリパフォーマンスにも影響を与えることがあります。ほとんどのカラムの値が連続的に書き込まれるような順序キーを選択すると、選択された圧縮アルゴリズム(およびコーデック)がデータをより効果的に圧縮できます。
テーブル内のすべてのカラムは、指定された順序キーの値に基づいてソートされます。これは、そのキー自体に含まれているかどうかにかかわらず適用されます。例えば、
CreationDate
をキーとして使用すると、他のすべてのカラムの値の順序はCreationDate
列の値の順序に対応します。複数の順序キーを指定することができ、これはSELECT
クエリのORDER BY
句と同じ意味で順序付けが行われます。
順序キーの選択
順序キーを選択するための考慮事項と手順について、posts
テーブルを例にして こちら を参照してください。
データモデリング技術
BigQuery から移行するユーザーは、ClickHouse でのデータモデリングのガイド を読むことをお勧めします。このガイドでは、同じ Stack Overflow データセットを使用して、ClickHouse の機能を利用した複数のアプローチを探ります。
パーティション
BigQuery のユーザーは、大規模なデータベースのパフォーマンスと管理性を向上させるために、テーブルをパーティションと呼ばれる小さく管理しやすい部分に分割するテーブルパーティショニングの概念に慣れているでしょう。このパーティショニングは、指定されたカラム(例えば日付)の範囲、定義されたリスト、またはキーに対するハッシュを用いて実現できます。これにより、管理者はデータを日付範囲や地理的な場所など、特定の基準に基づいて整理できます。
パーティショニングは、パーティションプルーニングを通じてデータへのアクセスを高速化し、効率的なインデクシングによってクエリパフォーマンスを向上させるのに役立ちます。また、バックアップやデータの削除などのメンテナンスタスクでも、全体のテーブルではなく個々のパーティションに対する操作を行うことができます。さらに、パーティショニングは、複数のパーティションに負荷を分散することによって BigQuery データベースのスケーラビリティを大幅に向上させることができます。
ClickHouse では、テーブルが初めて定義されるときに PARTITION BY
句を使用してパーティショニングを指定します。この句には、SQL 式を任意のカラムに対して含めることができ、その結果が行が送信されるパーティションを定義します。

データパーツはディスク上の各パーティションと論理的に関連付けられ、独立してクエリされます。以下の例では、toYear(CreationDate)
の式を使用して posts
テーブルを年ごとにパーティション化します。行が ClickHouse に挿入されると、この式は各行に対して評価され、行はそのパーティションに属する新しいデータパーツとしてルーティングされます。
アプリケーション
ClickHouse のパーティショニングは、BigQuery と同様のアプリケーションを持ちますが、いくつかの微妙な違いがあります。具体的には:
- データ管理 - ClickHouse では、ユーザーはパーティショニングをデータ管理機能と見なすべきです。キーに基づいてデータを論理的に分離することにより、各パーティションは独立して操作できる(例えば削除できる)ことを意味します。これにより、ユーザーはパーティションを移動させることができ、特定の条件に基づいて ストレージ階層間で効率的に移動 したり、データを期限切れにして効果的に削除したりできます。例えば、以下のように 2008 年の投稿を削除することができます:
- クエリ最適化 - パーティションはクエリパフォーマンスの向上を助けることがありますが、これはアクセスパターンに大きく依存します。クエリが特定のいくつかのパーティション(理想的には1つ)をターゲットにする場合、パフォーマンスは向上する可能性があります。これは、パーティショニングキーが主キーに含まれていなく、フィルタリングによって使用される場合にのみ一般的に有益です。ただし、多くのパーティションをカバーする必要があるクエリは、パーティショニングを使用しない場合よりもパフォーマンスが悪くなる場合があります(パーティショニングの結果としてパーツが増加する可能性があるためです)。単一のパーティションをターゲットにする利点は、パーティショニングキーがすでに主キーの初期エントリーである場合には顕著ではなくなります。パーティショニングは、各パーティションの値が一意である場合に GROUP BY クエリを最適化 にも使用できます。ただし、一般的に、ユーザーは主キーが最適化されていることを確認し、特定の予測可能なサブセットへのアクセスパターンがある特別なケースでのみパーティショニングをクエリ最適化技術と考慮すべきです。
お勧め
ユーザーは、パーティショニングはデータ管理技術として考えるべきです。これは、タイムシリーズデータを扱う際にクラスターからデータを期限切れにする必要がある場合に理想的です。例えば、最も古いパーティションは 単に削除 できます。
重要: パーティショニングキーの式が高いカーディナリティのセットを生成しないことを確認してください。すなわち、100 を超えるパーティションを作成しないようにしてください。例えば、クライアント識別子や名前のような高いカーディナリティのカラムでデータをパーティショニングしないでください。代わりに、クライアント識別子や名前を ORDER BY
式の最初のカラムにします。
内部的に、ClickHouse は挿入されたデータのために パーツを作成 します。データが追加されると、パーツの数は増加します。過度に高い数のパーツを防ぐために、これはクエリパフォーマンスを低下させる(読み取るファイルが多くなるため)ので、パーツはバックグラウンドの非同期プロセスで統合されます。パーツの数が 事前設定された制限 を超えると、ClickHouse は挿入時に "too many parts" エラーとして例外をスローします。これは通常の操作では発生せず、ClickHouse が誤設定されているか、正しく使用されていない場合(例えば、小さな挿入が多い場合)にのみ発生します。パーツは各パーティションごとに独立して作成されるため、パーティションの数を増やすとパーツの数も増加します。したがって、高いカーディナリティのパーティショニングキーはこのエラーを引き起こす可能性があるため、回避すべきです。
マテリアライズドビューとプロジェクション
ClickHouse のプロジェクションの概念により、ユーザーはテーブルに対して複数の ORDER BY
句を指定できます。
ClickHouse データモデリング の中で、マテリアライズドビューが ClickHouse で集約を事前計算したり、行を変換したり、さまざまなアクセスパターン向けにクエリを最適化する方法について説明します。後者については、ここに例を示しました。マテリアライズドビューは、元のテーブルへの挿入と異なる順序キーを持つターゲットテーブルに行を送信します。
例えば、以下のクエリを考えてみてください。
このクエリでは、UserId
が順序キーではないため、9000 万件のすべての行をスキャンする必要があります(迅速ですが)。以前は、PostId
のルックアップとして機能するマテリアライズドビューを使用してこの問題を解決しました。同じ問題はプロジェクションを使用して解決できます。以下のコマンドは ORDER BY user_id
のプロジェクションを追加します。
まずプロジェクションを作成してからマテリアライズする必要があることに注意してください。この後者のコマンドは、データを二回ディスクに保存し、二つの異なる順序にします。プロジェクションは、データが作成されたときに以下のように定義され、自動的にメンテナンスされます。
プロジェクションが ALTER
コマンドによって作成される場合、MATERIALIZE PROJECTION
コマンドが発行されたときに作成は非同期的です。ユーザーは以下のクエリでこの操作の進行状況を確認でき、is_done=1
になるのを待ちます。
上記のクエリを繰り返すと、パフォーマンスが大幅に向上したことが確認できますが、追加のストレージの代償があります。
EXPLAIN
コマンドを使用して、このクエリがプロジェクションを使用してサーブされたことを確認します。
プロジェクションを使用するタイミング
プロジェクションは、新しいユーザーにとって魅力的な機能です。なぜなら、データが挿入されると自動的に維持されるからです。さらに、クエリは可能な限りプロジェクションが活用される単一のテーブルに送信され、応答時間を短縮します。

これは、マテリアライズドビューとは対照的です。マテリアライズドビューでは、ユーザーが適切な最適化されたターゲットテーブルを選択するか、フィルタに応じてクエリを再構築する必要があります。これは、ユーザーアプリケーションにより多くの重視を置き、クライアント側の複雑性を増加させます。
これらの利点にもかかわらず、プロジェクションにはいくつかの固有の制限があり、ユーザーはそれを理解した上で慎重に展開すべきです。詳細については、"マテリアライズドビューとプロジェクション"を参照してください。
プロジェクションを使用することをお勧めするのは次のような場合です:
- データの完全な再順序が必要な場合。プロジェクション内の式は理論的には
GROUP BY
を使用することができるが、マテリアライズドビューは集計を維持するのにより効果的です。クエリオプティマイザーも、単純な再順序を使用するプロジェクションを利用する可能性が高く、すなわちSELECT * ORDER BY x
となります。ユーザーは、この式の中でストレージフットプリントを削減するために、カラムのサブセットを選択できます。 - ユーザーがストレージフットプリントの増加やデータを二回書くオーバーヘッドに対して快適である場合。挿入速度への影響をテストし、ストレージオーバーヘッドを評価します。
BigQuery のクエリを ClickHouse で書き直す
以下は、BigQuery と ClickHouse を比較した例クエリです。このリストは、ClickHouse の機能を利用してクエリを大幅に簡素化する方法を示すことを目的としています。ここでの例は、Stack Overflow のデータセット全体(2024年4月まで)を使用しています。
最も多くのビューを受けたユーザー(10件以上の質問を持つ):
BigQuery

ClickHouse
最も多くのビューを受けたタグ:
BigQuery

ClickHouse
集約関数
可能な限り、ユーザーは ClickHouse の集約関数を活用すべきです。以下では、argMax
関数を使用して、各年の最も閲覧された質問を計算する方法を示します。
BigQuery


ClickHouse
条件文と配列
条件文と配列関数はクエリを大幅に簡素化します。以下のクエリは、2022 年から 2023 年にかけて最も大きなパーセンテージの増加を持つタグ(10000 件以上の出現)を計算します。以下の ClickHouse クエリは、条件文、配列関数、HAVING
および SELECT
句でのエイリアスの再利用が可能であるため、簡潔です。
BigQuery

ClickHouse
これで、BigQuery から ClickHouse への移行に関する基本ガイドが終了します。BigQuery から移行するユーザーは、ClickHouse の データモデリング ガイドを読むことで、ClickHouse の高度な機能についてさらに学ぶことをお勧めします。