CockroachDBマルチリージョンガイド:テーブルローカリティ、遅延、耐障害性の設計
CockroachDBは、SQLデータベースを複数リージョンで動かせます。ただし、マルチリージョンは魔法ではありません。データをユーザーの近くに置けば遅延を下げられ、耐障害性も高められます。一方で、書き込み遅延、トランザクション競合、運用の複雑さ、データ所在地の設計が必要になります。
このガイドでは、CockroachDBマルチリージョン設計の実践的な判断を扱います。どのリージョンを追加するか、テーブルローカリティをどう選ぶか、regional by rowやglobal tableをいつ使うか、本番前に何を検証するかを説明します。
参考ドキュメント:
- CockroachDB multi-region overview
- Choosing a multi-region configuration
- Table localities
- Multi-region survival goals
最初に決めるべきこと
SQLを変更する前に、アプリケーションが何を最も必要としているかを決めます。
| 目的 | 設計方向 | トレードオフ |
|---|---|---|
| リージョン内ユーザーの読み取り遅延を下げる | データを主な読み取りリージョンの近くに置く | クロスリージョン書き込みは遅くなる場合がある |
| データ所在地要件 | リージョン配置とデータ所有を明確にする | Schemaとルーティングを厳密に扱う必要がある |
| グローバル参照データ | 読み取り中心の小さな表にglobal tableを使う | 書き込みは慎重に扱う |
| リージョン障害への耐性 | survival goalとトポロジーを選ぶ | レプリカと調整が増え、遅延に影響する場合がある |
| 多リージョンのアクティブユーザー | 近いリージョンへルーティングし、データ所有をモデル化する | 競合する書き込みはアプリ側でも扱う |
良いマルチリージョン設計は、地理だけでなくアクセスパターンから始まります。
基本概念
CockroachDBのマルチリージョン抽象は、次の概念で構成されます。
- Cluster region:ノード起動時のlocality設定で指定するリージョン。
- Database region:データベースに追加されるリージョン。そのうち1つがprimary region。
- Primary region:テーブルローカリティで別に指定しない限り、データベースの既定となるリージョン。
- Table locality:CockroachDBがテーブルデータをどこに置き、どう最適化するか。
- Survival goal:データベースが耐えることを目指す障害範囲。
- Secondary region:対応する構成でフェイルオーバーに関わるリージョン。
これらは単なるデプロイラベルではありません。性能と可用性に直接影響します。
基本的なマルチリージョン設定
マルチリージョンクラスターは、ノードのlocalityから始まります。各ノードは自分のregionとzoneを知る必要があります。
cockroach start \
--locality=region=us-east-1,zone=us-east-1a \
--certs-dir=certs \
--join=node1:26257,node2:26257,node3:26257
次にデータベースリージョンを設定します。
ALTER DATABASE global_app SET PRIMARY REGION "us-east-1";
ALTER DATABASE global_app ADD REGION "eu-west-1";
ALTER DATABASE global_app ADD REGION "ap-southeast-1";
SHOW REGIONS FROM DATABASE global_app;
これだけで、すべてのクエリがすべての地域で速くなるわけではありません。リージョンの土台を作るだけです。ユーザー体感は、主にテーブルローカリティとアプリケーションルーティングで決まります。
テーブルローカリティを選ぶ
テーブルローカリティは、データ配置と最適化の方法を決めます。
| Locality | 向いている場面 | メモ |
|---|---|---|
| Regional by table | 表全体が主に1つのリージョンに属する | リージョン専用データに単純 |
| Regional by row | 行ごとに所属リージョンが違う | ユーザー、テナント、アカウントデータに向く |
| Global | 小さく読み取り中心の参照データ | 読み取りに便利だが、書き込みは慎重に |
Regional By Table
表全体が1つのリージョンにほぼ属する場合は、regional by tableを使います。
CREATE TABLE eu_tax_rules (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
country STRING NOT NULL,
rule JSONB NOT NULL
) LOCALITY REGIONAL BY TABLE IN "eu-west-1";
地域ごとの設定、法規則、ローカルカタログ、地理に結びつく運用データに向いています。
Regional By Row
各行にhome regionがある場合は、regional by rowを使います。ユーザーやテナントデータでよく使います。
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email STRING NOT NULL,
home_region crdb_internal_region NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
) LOCALITY REGIONAL BY ROW;
アプリケーションは正しいhome regionを設定し、維持する必要があります。リージョン列は付属情報ではなく、データモデルの一部です。
Global Tables
global tableは、どの地域からも読まれる小さな参照データに向いています。
CREATE TABLE countries (
code STRING PRIMARY KEY,
name STRING NOT NULL
) LOCALITY GLOBAL;
国コード、ほとんど変わらない機能フラグ、公開設定、参照データなどが候補です。高頻度の取引書き込みに使う場合は、遅延と競合を必ずテストしてください。
トラフィックを意図的にルーティングする
データベース配置は設計の半分です。アプリケーションのトラフィックも、通常は近い健全なリージョンへ送る必要があります。
確認すること:
- 各ユーザーリクエストをどのアプリリージョンが処理するか。
- 接続プールがローカルのデータベースノードへ接続しているか。
- 読み取りがユーザーのhome regionに近いか。
- 書き込みがその行やテナントを所有するリージョンに送られているか。
- フェイルオーバー後のリージョンが安全にワークロードを処理できるか。
ヨーロッパのユーザーがヨーロッパのアプリサーバーにアクセスしても、そのサーバーが米国をhomeとする行を書き込むなら、クロスリージョン遅延は残ります。
トランザクション競合を扱う
マルチリージョンにしても、書き込み所有の設計は必要です。2つのリージョンが同じ行を頻繁に更新すれば、リトライや競合が起きます。
競合を減らす方法:
- テナント、アカウント、文書ごとに明確な書き込み所有者を決める。
- ホットなグローバルカウンターを避ける。
- 冪等なコマンドとリトライ可能なトランザクションロジックを使う。
- トランザクションを短くする。
- 書き込みが多いレコードを小さな所有単位に分ける。
- 読み取り中心の共有データは参照テーブルに分ける。
Serializable分離は有用ですが、アプリケーション側のリトライ戦略は必要です。
データ所在地
所在地要件がある場合、どのデータがどの地域に残るべきかを文書化します。
- ユーザープロファイル。
- 支払いまたは請求データ。
- ログと監査記録。
- バックアップ。
- 派生分析データ。
- 検索インデックスとキャッシュ。
主テーブルを正しく置くだけでは不十分です。コピー、エクスポート、ログ、バックアップ、下流システムも同じポリシーに従う必要があります。
Survival Goals
Survival goalは、データベースがどの障害に耐える設計かを定義します。より高い耐障害性には、より慎重なトポロジーが必要で、遅延にも影響することがあります。
確認すること:
- zone障害に耐えたいのか、region障害に耐えたいのか。
- 利用できるregionとzoneはいくつあるか。
- 障害中も書き込み可能であるべき表はどれか。
- 現在のtable localityと使いたい機能は互換性があるか。
- 実際のアプリケーショントラフィックでフェイルオーバーをテストしたか。
テストする前に、固定のRTOやRPOをアーキテクチャ文書で約束しないでください。
本番前チェックリスト
本番前に確認します。
- ノード起動時のregionとzoneが一貫している。
- database primary regionとsecondary regionが意図的に設定されている。
- 各表のlocalityがアクセスパターンに合っている。
- regional by row表に信頼できるリージョン割り当てがある。
- アプリケーションルーティングがデータ所有と一致している。
- トランザクションリトライが実装されている。
- バックアップと下流システムが所在地ルールを守る。
- スキーマ変更を適切なリージョンから実行している。
- 遅延、リトライ、leaseholder移動、unavailable rangeを監視している。
- フェイルオーバー訓練が文書化され、繰り返されている。
よくある失敗
すべての表をGlobalにする
Global tableは便利ですが、既定にするものではありません。小さく読み取り中心のデータに向いています。高書き込みの業務データでは慎重にテストします。
アプリケーション層を無視する
アプリケーショントラフィックがリージョン別にルーティングされていなければ、データベースローカリティだけで遅延は解決できません。
リトライロジックがない
分散SQLシステムでは、アプリケーションがリトライ可能なトランザクションエラーを扱う必要があります。リトライは明示的で、操作は冪等にします。
データ所在地を単一テーブル設定だと思う
所在地は、バックアップ、ログ、分析、検索、エクスポートまで含みます。データフロー全体を監査します。
障害テストをしない
マルチリージョン設計は、ノード、zone、regionの障害シナリオでテストするべきです。図が本番挙動を保証するわけではありません。
まとめ
CockroachDBのマルチリージョン設計は、データをユーザーの近くに置くこと、所在地要件を満たすこと、障害に耐えること、書き込み経路を理解可能にすることのバランスです。アクセスパターンとデータ所有から始め、テーブルローカリティを選び、リージョンを設定し、リトライを実装し、フェイルオーバーをテストします。
目的は「すべてをどこでもactive-activeにする」ことではありません。通常時とリージョン障害時の挙動を予測可能にすることです。
ブラウザローカルツールを無料で試す →