レプリケーション・分散
Primary-Replica、シャーディング、HA、フェイルオーバー、Read Replica の使い方、 分散 SQL DB(CockroachDB / Spanner)まで。
レプリケーションの目的
- HA(高可用性)— Primary が落ちても Replica が引き継ぐ
- Read 拡張 — 読み取りを Replica に分散
- 地理分散 — 各リージョンに Replica
- 分析専用 — 本番に影響を与えず集計
- バックアップ — Replica からダンプ
方式
1. Primary-Replica(Master-Slave)
[Primary] ─── WAL ───> [Replica 1]
[Replica 2]
[Replica 3]
書き込み: Primary のみ
読み取り: 全部
- 最も一般的
- Replica は Read Only
- Primary 障害時は Replica を昇格
2. Multi-Primary(Multi-Master)
- 複数の Primary が同時書き込み可
- 競合解決が複雑
- Postgres は標準では非対応(BDR / pgEdge 等の拡張)
- Cassandra / DynamoDB は元々これ
3. レプリカ無し(単一 DB)
- 個人開発 / 小規模ならこれで OK
- マネージド DB は裏で自動でレプリカを持っていることが多い
Postgres のレプリケーション
物理レプリケーション(Streaming Replication)
- WAL(Write-Ahead Log)を Replica に送信
- Primary と完全に同じバイナリ
- 標準で速い、推奨
- Replica はRead Only
論理レプリケーション
- SQL レベル(INSERT/UPDATE/DELETE のイベント)を送信
- テーブル単位 / カラム単位で選べる
- 異なるバージョンの Postgres 間でも可
- Supabase Realtime が裏で使っている
同期 vs 非同期
| 方式 | 挙動 |
|---|---|
| 非同期(既定) | Primary が COMMIT 後、非同期で Replica へ送信。データ損失の可能性 |
| 同期 | Replica の書き込みも待ってから COMMIT。安全だが遅い |
| Quorum | N 台中 M 台が ACK したら COMMIT |
レプリカラグ
- 非同期だと Primary との遅延がある(数 ms 〜 数秒)
- 「書いた直後に読む」と古いデータが返る
- 対策: 書き込み直後だけ Primary から読む(Read Your Writes)
- 監視:
SELECT pg_wal_lsn_diff(...)
Read Replica の使い方(アプリ側)
// 書き込み
await primary.query("INSERT ...")
// 読み取り(重い分析、最新性が要らない)
await replica.query("SELECT ...")
// Read Your Writes が必要なケース
await primary.query("SELECT ...")
Prisma は readReplicas 拡張で振り分け可能。
シャーディング
データを複数のサーバに水平分割する。1 台で持ちきれない量に対応。
ユーザ ID で分割(hash sharding)
user 1〜1000 → Shard A
user 1001〜2000 → Shard B
user 2001〜3000 → Shard C
シャーディングの方式
- Hash sharding: ハッシュで割り振る(均等)
- Range sharding: 範囲で分ける(時系列向き)
- Geo sharding: 地域で分ける(地理分散)
- Directory-based: 専用テーブルで対応表を持つ(柔軟・複雑)
難しいところ
- クロスシャード結合が困難
- シャードキーが偏るとホットスポット
- 再シャーディング(リバランス)が高コスト
- 運用負荷が跳ねる
Postgres のシャーディング
- Citus — Postgres 拡張、最有力
- pg_shard / shardman
- Vitess — MySQL 系(YouTube が開発)
分散 SQL DB(NewSQL)
SQL を保ったまま自動でシャーディング + レプリケーション + 強整合。
- CockroachDB — Postgres 互換、Raft ベース
- Google Spanner — TrueTime(GPS+原子時計)
- YugabyteDB
- TiDB — MySQL 互換、HTAP
書き込み性能を求めて結局 Postgres + Citus / マネージド分散 DB / NoSQL に行くケースが増えている。
HA(高可用性)
RPO / RTO
- RPO(Recovery Point Objective)— 失っていいデータの量(時間)
- RTO(Recovery Time Objective)— 復旧までに許容する時間
典型構成
[Primary]
↓ 同期レプリケーション
[Sync Replica] ← 自動フェイルオーバー
↓ 非同期
[Async Replica 1, 2]
フェイルオーバー
- Primary 障害を検知 → Replica を Primary に昇格
- VIP(仮想 IP)/ DNS / プロキシで切替
- マネージド DB(Supabase / Neon / RDS)は自動
- 自前ならPatroni / repmgr
マネージド DB の比較
| サービス | 特徴 |
|---|---|
| AWS RDS / Aurora | 定番、Aurora は読み拡張に強い |
| Google Cloud SQL / AlloyDB | GCP 統合 |
| Azure Database | Microsoft 環境 |
| Supabase | BaaS としてのフルスタック |
| Neon | Serverless Postgres、ブランチング |
| PlanetScale | MySQL ベース、ブランチング |
| Crunchy Bridge | シンプルな Postgres ホスティング |
| Render / Railway | 個人〜小規模 |
| Vercel Postgres | Vercel と密結合 |
| Prisma Postgres | Prisma 公式 |
バックアップ戦略
- 定期フル + WAL アーカイブ — Point-in-Time Recovery
- 論理ダンプ(pg_dump)— 別 DB に持ち運べる
- 地理冗長 — 別リージョンにも保管
- 世代管理(毎日 7 世代、毎週 4 週、毎月 12 ヶ月)
- 復元演習を年 1 回はやる
地理分散の選択肢
- 単一リージョン + CDN: 多くのケースはこれで足りる
- Read Replica を各リージョン: 読みは速い、書きは中央へ
- Multi-Primary: 書きも各地で(DynamoDB Global Tables / CockroachDB / Spanner)
- Edge Database: Turso / Cloudflare D1 / Fauna
CDC(Change Data Capture)
DB 変更をイベントストリームとして外部に流す:
- Debezium(Kafka 連携)
- Postgres 論理レプリケーション
- Supabase Realtime / Prisma Pulse
用途: 検索 Index 同期、アナリティクス、マイクロサービス連携。
Connection Pooling と分散
- Replica 増やしても接続数は別問題
- PgBouncer / Supavisor / pgcat で吸収
- Serverless は特に重要
失敗パターン
| 症状 | 原因 |
|---|---|
| 書いた直後に読めない | レプリカラグ。Primary から読む |
| Primary 落ちて全停止 | HA 設計してない / フェイルオーバー手順なし |
| シャーディング後に集計が遅い | クロスシャードクエリ / OLAP は別 DB へ |
| WAL 溜まりすぎ | Replica 接続切れたまま放置 |
| バックアップから戻せない | 復元演習未実施。実際に試す |
運用 Tips
- マネージド推奨: 自前運用は専門人材必須
- HA / バックアップは仕様で先に決める(後付けは大変)
- 監視: ラグ、接続数、ディスク、CPU
- 大規模化する前にRead Replica + キャッシュ + CDN の三段で稼げないか検討
- 「シャーディング」は最終手段
原則
大半のサービスは「単一 Postgres + Read Replica + Redis キャッシュ」で十分。 シャーディング / 分散 SQL に手を出す前に、Read 分散とキャッシュで稼げるかを試す。 そこを超えてから初めて分散 DB を検討。