シャーディングの苦悩から解放されるSQLのスケーリング:CockroachDBガイド

Database tutorial - IT technology blog
Database tutorial - IT technology blog

スケーリングの壁:従来のSQLが限界を迎える理由

データベースのスケーリングは、通常、予測可能かつ苦痛を伴うプロセスを辿ります。アプリケーションのトラフィックが10倍に急増すると、突然メインのPostgreSQLやMySQLインスタンスのCPU使用率が100%に張り付きます。最初の直感は垂直スケーリング(128コアや2TBのRAMを搭載したより強力なインスタンスへのアップグレード)でしょう。しかし、最終的にはハードウェアコストが天文学的な数字になるか、単一マシンの物理的な限界に達するという、高い壁に突き当たります。

手動シャーディングは伝統的な「解決策」ですが、多くの場合、解決しようとしている問題よりも事態を悪化させます。user_idのようなキーに基づいて、複数のデータベースインスタンスにデータを分割することになります。これにより、アプリケーションロジックでシャードの場所を追跡する必要が生じ、シャード間を跨ぐ結合(JOIN)はほぼ不可能になり、ACIDトランザクションは分散システムの悪夢へと変わります。CockroachDBは、まさにこの複雑さを排除するために構築されました。

クイックスタート:5分でローカルクラスターを実行する

CockroachDBは、最初から分散化を想定して設計されています。1台のノートPC上でも、マルチノードクラスターをシミュレートして、データのレプリケーションやフェイルオーバーの仕組みを確認できます。Dockerを使えば、3ノードのクラスターを数秒で立ち上げることが可能です。

まず、ノード間が通信できるようにブリッジネットワークを設定します。

docker network create -d bridge roachnet

次に、3つのノードを起動します。ここで重要なのが--joinフラグです。これは、各ノードが他のノードを見つけて1つの論理ユニットを形成するための「秘伝のソース」となります。

# ノード 1
docker run -d --name=roach1 --hostname=roach1 --net=roachnet -p 26257:26257 -p 8080:8080 cockroachdb/cockroach:v23.1.10 start --insecure --join=roach1,roach2,roach3

# ノード 2
docker run -d --name=roach2 --hostname=roach2 --net=roachnet cockroachdb/cockroach:v23.1.10 start --insecure --join=roach1,roach2,roach3

# ノード 3
docker run -d --name=roach3 --hostname=roach3 --net=roachnet cockroachdb/cockroach:v23.1.10 start --insecure --join=roach1,roach2,roach3

いずれか1つのノードで次のコマンドを実行して、クラスターを初期化します。

docker exec -it roach1 ./cockroach init --insecure

これで分散型SQLデータベースが稼働しました。http://localhost:8080にアクセスしてDBコンソールを確認してください。ここでは、ノードの健全性やクエリのスループットに関するリアルタイムのメトリクスが表示されます。標準的なPostgresクライアントや組み込みのシェルを使用して接続できます。

docker exec -it roach1 ./cockroach sql --insecure

エンジン:CockroachDBによるデータ管理の仕組み

一般的なデータベースでは、フォロワーが単なる待機系(ウォームスタンバイ)となるリーダー・フォロワーモデルがよく使われます。CockroachDBは、**Raftコンセンサスアルゴリズム**を使用してこの常識を覆します。すべてのノードを、読み取りと書き込みの両方を処理できる一級市民として扱います。

データは「レンジ(range)」と呼ばれる64MBのチャンクに分割されます。各レンジは少なくとも3つのノードに複製されます。書き込みが発生すると、「リースホルダー(Leaseholder)」ノードが、過半数のレプリカが変更を承認したことを確認してから確定します。ノードが故障した場合、クラスターは不足したデータを自動的に再複製し、設定されたレプリケーション係数を維持します。これはバックグラウンドで行われ、手動の介入は一切不要です。

PostgreSQLとの互換性

CockroachDBはPostgreSQLのワイヤープロトコル(バージョン3.0)を使用しているため、TypeORM、Sequelize、GORMなどのツールをそのまま利用できます。カスタムドライバーは必要ありません。ただし、Postgresのフォークではなく、Goで記述されており、シリアライザブル(Serializable)な分離レベルを優先しています。これにより、最高レベルのデータ一貫性が提供され、弱い分離レベルで発生しがちな競合状態を防ぐことができます。

データインポートの効率化

レガシーシステムからの移行では、データの形式が複雑なことがよくあります。スキーマ移行のために生のデータセットを素早く変換する必要がある場合、私はtoolcraft.app/ja/tools/data/csv-to-jsonを使用しています。これはブラウザ上ですべてを処理するため、インポートスクリプトのプロトタイプを作成している間も、機密データを外部サーバーに送らずに済みます。

高度な活用法:リージョン障害への対策

グローバルなアプリケーションでは、単一サーバーのクラッシュに耐えるだけでは不十分です。AWSやGCPのリージョン全体がダウンしても稼働し続ける必要があります。CockroachDBは、ハードウェアの物理的な地理情報を定義できる**ロカリティフラグ(Locality Flags)**によってこの問題を解決します。

本番環境では、起動時にノードの場所を定義します。

cockroach start \
--certs-dir=certs \
--locality=region=us-east,zone=us-east-1a \
--join=node1.example.com,node2.example.com \
--advertise-addr=node1.example.com

これらのフラグにより、スマートなデータ配置が可能になります。例えば、ヨーロッパのユーザーデータをフランクフルトやロンドンのノードに固定しつつ、米国のデータはニューヨークに保持するといったことが可能です。これにより、ローカルユーザーのレイテンシを下げて「光速の壁」問題を解決しながら、開発者にとっては単一の統合されたデータベースを維持できます。これは、地理分散システムの「聖杯」とも言えるソリューションです。

本番環境運用のための教訓

分散システムの管理は、単一のVPSを管理するのとは全く別物です。現場で得られた重要な教訓をいくつか紹介します。

  • TLSの強制: --insecureフラグはローカル開発専用です。本番環境では、cockroach certコマンドを使用してノードおよびクライアント証明書を生成し、ノード間のすべての通信を暗号化してください。
  • 時刻同期(クロックスキュー)に注意: 分散データベースの生命線は時刻同期です。ノード間の時刻のずれ(オフセット)が500ミリ秒を超えると、CockroachDBはデータ破損を防ぐためにプロセスを停止します。chronyやNTPを使用して、時刻のずれを数ミリ秒以内に保つようにしてください。
  • 自動インクリメント(Auto-Increment)を避ける: 主キーに連続した整数を使用すると、すべての新しい書き込みが単一のノードに集中する「ホットスポット」が発生します。UUIDhash_sharded_indexを使用して、負荷をクラスター全体に分散させましょう。
  • すべてをロードバランスする: アプリケーションを単一ノードのIPに向けないでください。HAProxyやNginxのストリームを使用して、接続を分散させます。これにより、1つのノードがダウンしても、アプリケーションはそれに気づくことさえありません。

パフォーマンスチューニング

クエリが遅いと感じる場合は、EXPLAIN ANALYZEの出力を確認してください。分散環境では、フルテーブルスキャンは複数のネットワーク往復を必要とするため、非常にコストが高くなります。適切なインデックス作成は単なる推奨事項ではなく、10ミリ秒のレスポンスか、2秒のタイムアウトかの分かれ目になります。

CockroachDBは、スケーリングへの恐怖を事実上なくしてくれるエンジニアリングの驚異です。データレイヤーの再設計をすることなく、小規模から始めて数百万人のユーザー規模まで成長させることができます。

Share: