DATABASE / DISTRIBUTED SYSTEMS
分散データベース入門シャーディング・レプリケーション・2相コミットを図解で理解する
ネットショップで「注文が確定しました」と表示されたのに、別の画面には古い在庫数が残っている。複数のデータベースにまたがる更新が、片方だけ成功した。データを分散させると便利になる一方、こうした問題が生まれます。分散配置・複製・トランザクションを順番に理解しましょう。
011台のデータベースでは何が困る?
小さなネットショップなら、商品・在庫・注文・会員を1台のデータベースで管理できます。ところが利用者が増えると、読み書きが集中し、バックアップや障害復旧にも負担がかかります。1台の故障でサービス全体が止まる構成も不安です。
そこでデータを複数のサーバーで扱う「分散データベース」を検討します。分散とは単にサーバーを増やすことではなく、どのデータをどこに置くか、更新をどう伝えるか、故障時に何を保証するかを決める設計です。
02分散データベースの全体像
| 技術 | 解決したい問題 | 大まかな仕組み |
|---|---|---|
| シャーディング | データ量・書き込み負荷を分散 | 注文IDや顧客IDで保存先を分ける |
| レプリケーション | 読み取り負荷分散・冗長化 | 同じデータのコピーを別ノードに保持 |
| 分散トランザクション | 別ノードの更新結果の不一致を防ぐ | 複数の更新を一括成功・失敗として調停 |
これらは排他的な技術ではありません。各シャードをさらに複製する構成も一般的です。一方で、複製先が増えたからといって複数シャードの更新が自動的に原子的になるわけではありません。
04レプリケーション:同じデータを複製する
レプリケーション(Replication)は、プライマリの更新内容をレプリカへ伝え、同じデータを複数ノードに保持する仕組みです。読み取りをレプリカへ分散したり、プライマリ障害時の切替先を用意したりできます。
代表的な構成では、書き込みをプライマリが受け、WAL(Write-Ahead Log)などの変更記録をレプリカへ転送して反映します。実装により物理・論理レプリケーションなどがあります。重要なのは複製が「いつ完了した」と扱われるかです。
05非同期レプリケーションの流れと落とし穴
非同期レプリケーションでは、プライマリがローカルの確定条件を満たすと、レプリカの反映完了を待たずに利用者へ成功を返せます。そのため書き込み応答を比較的速くしやすい一方、プライマリとレプリカの間に短い差が生まれます。
プライマリに書き込む
商品在庫を10個から9個に変更し、プライマリでCOMMIT。
先に成功を返す
レプリカの更新を待たずに『注文完了』を返す。
あとから反映
変更ログが転送され、レプリカの在庫数も9個になる。
転送前にレプリカを読むと、まだ10個と表示されることがあります。これがレプリカ遅延(Replication Lag)です。さらに、プライマリが停止し、まだ変更を受け取っていないレプリカを昇格させると、利用者には成功と返した直近の更新が失われる可能性があります。
06同期レプリケーションとの違い
同期レプリケーションは、設定されたレプリカから所定の確認応答が返るまで書き込み完了の応答を待つ方式です。たとえば「レプリカがWALを永続化した」ことを確定条件にすれば、プライマリ単独障害時のデータ損失リスクを下げられます。ただし通信往復やレプリカの状態が書き込み待ち時間に影響します。
| 比較 | 非同期レプリケーション | 同期レプリケーション |
|---|---|---|
| 書き込み応答 | レプリカの完了を待たずに返せる | 指定したレプリカの確認を待つ |
| 遅延 | 通常は抑えやすい | ネットワーク往復の影響を受ける |
| 切替時の損失 | 未転送分を失う可能性 | 確認済み範囲の耐久性を高められる |
| レプリカ障害 | 基本的に書き込みを継続しやすい | 必要な応答を得られず待機する場合がある |
| 用途 | 読み取り分散、多少の遅延を許容 | 確定済み更新の耐久性を重視 |
なお「同期」といっても、転送済み・ディスクへ永続化済み・問い合わせから参照可能、のどこまで待つかで保証が変わります。PostgreSQLの remote_write、on、remote_apply などがその例です。
07障害とフェイルオーバー
プライマリが故障し、レプリカを新しいプライマリとして昇格させる処理をフェイルオーバーといいます。ただし切り替えるだけでは安全とは限りません。旧プライマリが復帰して両方が書き込みを受ける「スプリットブレイン」を避けるため、リーダー選出やフェンシング(旧リーダーの書き込み禁止)などが必要です。
| 障害例 | 起こり得る問題 | 設計上の対策 |
|---|---|---|
| 非同期転送の遅れ | 昇格先に最新のデータがない | 遅延監視、許容損失量の定義 |
| プライマリ停止 | 書き込み停止・切替待ち | 監視、昇格判断、切替手順 |
| ネットワーク分断 | 複数ノードが自分を主系と判断 | 適切なリーダー選出、フェンシング |
| 古いレプリカへの読み取り | 直前の変更が見えない | 重要な読み取りは主系へ、整合性要件に応じた読取制御 |
08整合性・可用性・通信遅延を考える
「同じデータを複数台に持つ」だけで、どのサーバーから読んでも常に同じ値になるわけではありません。非同期複製では時間の経過とともに値が収束する結果整合性(Eventual Consistency)が典型的な選択肢です。ただしアプリケーションは、更新直後に自分の更新が見えることを要求する場合があります。
一方で、異なるノードを含む取引の原子性、読み取りの新鮮さ、サービスが応答できることは、それぞれ別の保証です。ネットワーク分断時は整合性と可用性の両立に制約が生じますが、CAP定理だけで具体的なDBの挙動を丸ごと説明することはできません。どの操作に何を保証するのかを確認する必要があります。
09複数DBをまたぐ注文処理の難しさ
ECサイトの注文を考えます。注文DBに「注文済み」と記録し、別の在庫DBから1個減らす必要があるとします。2台をそれぞれ独立にCOMMITすると、最初のDBだけ成功して次で障害が起きるかもしれません。
注文DB
注文 #101 を『確定』で登録する。
在庫DB
在庫10個から9個に減らす。
途中で障害
注文だけ確定して在庫が残ると業務上の不整合が発生。
単一のリレーショナルDB内であれば通常は1つのローカルトランザクションでまとめられます。しかし独立したDBやシャードをまたいで、全体でAll or Nothing(全部成功または全部中止)を必要とするなら、分散トランザクションの調停が課題になります。
102相コミット:全体の確定をそろえる
2相コミット(Two-Phase Commit、2PC)は、調停者(Coordinator)が各参加者(Participant)へ確認を取り、全参加者のトランザクションをまとめてCOMMITまたはABORTするための手順です。
| 段階 | 調停者 | 参加者 |
|---|---|---|
| 第1相:Prepare(準備・投票) | 各DBに『確定できるか』問い合わせる | 更新を準備・必要なログを永続化し、YES/NOを返す |
| 第2相:Commit / Abort(最終決定) | 全員YESならCOMMIT、それ以外は原則ABORTを決め、記録して通知 | 通知に従って確定または中止し、完了を返す |
「YES」はCOMMIT済みではありません。「最終決定が来ればCOMMITできる」という準備完了の約束です。全員YESでも決定通知を待つ必要があります。
11調停者が止まるとどうなる?
注文DBと在庫DBがYESを返した直後、調停者が停止したとします。どちらのDBも、全体でCOMMITするのかABORTするのかをまだ知らない可能性があります。自分で勝手にCOMMITやROLLBACKを選ぶと、別の参加者の結果と食い違う危険があります。
このためPrepared状態で待ち続けることがあり、ロックやリソースが保持されて後続処理を妨げます。これを2PCのブロッキング問題と呼びます。調停者の決定ログや参加者の状態を確認し、定められた復旧手順に従う必要があります。
12レプリケーションと2PCは別の問題を解く
| 方式 | 主に何をそろえる? | いつ待つ? | 注意点 |
|---|---|---|---|
| 非同期レプリケーション | 同じデータのコピー | 複製完了を通常は待たない | 遅延、古い読み取り、未複製分の損失 |
| 同期レプリケーション | コピーの所定の受信・永続化等 | 指定レプリカからのACKまで | 待ち時間、レプリカ不調による停止 |
| 2相コミット | 別々の更新参加者の確定/中止 | 準備票と最終決定を調停 | Prepared中のブロッキング、復旧管理 |
シャーディングは配置、レプリケーションは複製、2PCは更新の原子性を扱います。3つを同時に利用することもあれば、どれかだけを利用することもあります。
したがって「レプリケーションが非同期で、2PCが同期」と単純に二分するのは正確ではありません。非同期/同期という区分はレプリケーション自身にもあり、2PCの「2相」はPrepareとCommit/Abortという決定手順を指します。
13Saga・Outbox・単一DBの選択肢
2PCを採用すれば、すべての設計が楽になるわけではありません。参加者全員が準備・確定手順に対応し、障害時に未解決のPreparedトランザクションを復旧できることが必要です。外部決済やメール配信など、すでに起きた現実の操作は厳密にROLLBACKできないこともあります。
| 選択肢 | 考え方 | 向いている場面・注意 |
|---|---|---|
| 単一DBトランザクション | 更新を1つのトランザクション境界に収める | 最も単純な候補。要件次第では分散不要 |
| 2PC | 全参加者のCOMMIT/ABORTを調停 | 厳密な一括確定が必要。長い待機やロックに注意 |
| Saga | ローカル確定をつなぎ失敗時に補償 | 業務的な取り消しを設計できる。瞬間的な不整合はあり得る |
| Transactional Outbox | 業務更新と送信予定イベントを同一DBで確定 | イベント送信の取りこぼしを抑える。受信側の冪等性も必要 |
例えば注文確定後にメールを送る処理まで2PCに含めるのは通常難しいため、Outboxと再送・重複排除の方が適切なことがあります。方式は厳密な同時確定が必要か、後から収束・補償できるかで選びます。
14操作ラボで違いを体験する
同じ「注文を確定する」という場面でも、複製を待つか・故障が起こるか・複数DBがYESを返すかで結果が変わります。まずレプリケーションの遅延と切替、次に2PCの投票とブロッキングを動かしてみてください。実DBへは接続しない概念シミュレーターです。
LAB 01 / REPLICATION
同じ注文を非同期・同期で処理してみる
注文を行い、レプリカへ変更を届ける前後の在庫数を見比べます。同期モードは「レプリカが反映したことを待つ」簡略モデルです。実製品では受信・永続化・適用のどこまで待つかは設定によって違います。
PRIMARY
プライマリDB
10個
書き込み元
REPLICA
レプリカDB
10個
ここまでの更新が反映されています
概念モデル上、在庫更新を仮置きして同期の待機を示しています。実DBではWALの永続化・待機中のトランザクション状態などがあり、このUIの数値表示と完全には一致しません。障害後の自動フェイルオーバーも実行しません。
LAB 02 / DISTRIBUTED TRANSACTION
2相コミットの準備・確定を追う
今度はレプリカではなく、別々の情報を持つ「注文DB」と「在庫DB」の両方を更新します。障害タイミングと在庫DBの応答を選んで、1段階ずつ進めてください。
調停者が注文DBと在庫DBにまたがる取引を開始します。
理解チェック
注文DBと在庫DBがYESを返したあと調停者が停止した場合、参加者が取るべき行動は?
15実務での設計チェック
| 確認事項 | 具体的な問い |
|---|---|
| 分割キー | 顧客・注文・商品、何を基準にシャードを切る? |
| 読み取りの新鮮さ | 更新直後はプライマリを読む必要がある? |
| 複製の耐久性 | 何件・何秒分の未複製データ損失を許容できる? |
| 切替の安全性 | 旧リーダーを確実に書き込み禁止にできる? |
| トランザクション境界 | 複数DBの厳密な原子性が本当に必要? |
| 未解決2PCの復旧 | 決定ログ、Prepared状態、ロック待ちを監視できる? |
| 代替案 | 単一DB、Saga、Outboxで要件を満たせない? |
DBの製品ごとに、複製の確認条件、リーダー選出、分散トランザクションの対応範囲は異なります。アーキテクチャ図だけでは保証は分からないため、公式仕様と障害試験で確かめます。
16まとめ
分散データベースの理解は、「データをどこに置くか」「コピーをいつそろえるか」「異なる更新をどう確定するか」の3つに分けると整理できます。シャーディングは分割、レプリケーションは複製、2相コミットは分散した更新の原子性を調停します。
非同期レプリケーションは応答を速くしやすい一方でレプリカ遅延や切替時の損失が課題です。同期レプリケーションは複製先の確認を待ちますが、その分遅延や停止の影響を受けます。2PCは全参加者の確定をそろえますが、Prepared後の障害で待機が長引く可能性があります。
参考資料
- PostgreSQL: Log-Shipping Standby Servers — Synchronous Replication(PostgreSQL Global Development Group)
- PostgreSQL: Two-Phase Transactions(PostgreSQL Global Development Group)
- PostgreSQL: PREPARE TRANSACTION(PostgreSQL Global Development Group)
- Designing Data-Intensive Applications(Martin Kleppmann)