レプリケーション戦略: シングルリーダー・マルチリーダー・リーダーレスの使い分け

分散データベースで「同じデータを複数ノードに持つ」のがレプリケーションです。可用性・スケール・地理的レイテンシのいずれを取りに行くかで、方式選択が変わります。シングルリーダー / マルチリーダー / リーダーレスの3方式は仕組みも運用上の課題も大きく違うため、製品選定や障害設計の前に区別を整理しておく価値があります。

本記事では『データ指向アプリケーションデザイン』(以下 DDIA)第5章「レプリケーション」をベースに、3方式の仕組みと選択判断、レプリケーションラグや競合解決といった共通課題を整理します。

整合性モデル(強整合性・結果整合性・調整可能整合性)と W+R>N の詳細は『結果整合性とBASE特性: 強整合性とのトレードオフと調整可能整合性(W+R>N)』、CAP/PACELC による分類論は『CAP定理(CA/CP/AP)とPACELC定理(PA/EL・PA/EC・PC/EC)による分散データベース分類』で扱っています。

本記事は「どう書き込みを伝搬するか」という方式の話に絞ります。

レプリケーションが解決する3つの課題

DDIA はレプリケーションの目的を3つに整理しています。

  • 障害耐性
    • ノードが落ちてもサービスを続けられる(フェイルオーバー)
  • 読み取りスケール
    • 読み取り負荷を複数レプリカに分散できる
  • 地理的レイテンシ短縮
    • ユーザーの近くにレプリカを置くことで応答時間を縮める

3方式(シングルリーダー / マルチリーダー / リーダーレス)はこれらをどの優先順位で取るかで使い分けが決まります。

シングルリーダー方式

最も一般的な方式で、PostgreSQL(9.0以降)、MySQL、Oracle Data Guard、SQL Server AlwaysOn、MongoDB レプリカセット、Kafka、RabbitMQ 高可用性キューなどが採用しています。マスター・スレーブと呼ばれることもあります。

仕組み

1台のリーダー(マスター、プライマリ)が全ての書き込みを受け付け、変更内容をレプリケーションログとしてフォロワー(スレーブ、レプリカ)に送ります。フォロワーはログを順に適用してリーダーと同じ状態を再現し、読み取りはリーダーまたはフォロワーから行えます。

flowchart TD
    Write(["書き込み"]) --> Leader["リーダー"]
    Leader -- "レプリケーションログ" --> F1["フォロワー1"]
    Leader -- "レプリケーションログ" --> F2["フォロワー2"]
    Leader -- "レプリケーションログ" --> F3["フォロワー3"]
    Leader --> Read(["読み取り"])
    F1 --> Read
    F2 --> Read
    F3 --> Read

同期 vs 非同期 vs 準同期

リーダーがフォロワーへログを送るとき、フォロワーの書き込み完了を待つかどうかで3パターンに分かれます。

  • 同期レプリケーション
    • フォロワーの確認を待ってから書き込み成功を返す
    • データロスはないが、フォロワーが1台でも落ちると書き込みが止まる
  • 非同期レプリケーション
    • 待たずに即座に成功を返す
    • 書き込みが速いが、リーダーが障害でクラッシュしたタイミング次第で未伝搬の書き込みを失う可能性がある
  • 準同期レプリケーション
    • 1台だけ同期、残りは非同期
    • 同期1台が応答すれば書き込みが続けられ、データロスのリスクと書き込み停止リスクのバランスを取る

実運用では準同期型が選ばれることが多いです。

フェイルオーバーの難しさ

フォロワー障害は単純で、復旧後にリーダーから差分ログを取得して追いつけば済みます(キャッチアップリカバリ)。

問題はリーダー障害です。フェイルオーバーは(1)リーダーが落ちた検出、(2)新リーダーの選出、(3)システムの再設定という流れで進みますが、典型的な落とし穴が3つあります。

  • 新リーダーが古いリーダーの書き込みを欠く
    • 非同期レプリケーションでフォロワーが追いつく前に切り替えると、未伝搬の書き込みが失われる
  • スプリットブレイン
    • 切り替え後に旧リーダーが復活して両者がリーダーとして書き込みを受け付ける状態
    • データ破損の温床になる
  • 検出のタイムアウト調整
    • 短すぎれば誤検出でフェイルオーバーが暴発する
    • 長すぎれば障害時の停止時間が伸びる

このため自動フェイルオーバーは慎重に設計する必要があり、運用ツール(Patroni、Orchestrator など)が広く使われます。

強み・弱み

  • 強み
    • 書き込み窓口が1つなので競合が起きない
    • 実装が単純
    • 整合性が出しやすい
  • 弱み
    • リーダーが書き込みのボトルネック・単一障害点
    • フェイルオーバー時のデータロスとスプリットブレインのリスク

マルチリーダー方式

複数のリーダーが書き込みを受け付ける方式で、マスター・マスター、アクティブ・アクティブとも呼ばれます。MySQL Tungsten Replicator、PostgreSQL BDR、Oracle GoldenGate、CouchDB などで採用されています。

仕組み

各リーダーは書き込みを受けつつ、他のリーダーへも変更を伝搬します。各リーダーは他リーダーから見ればフォロワーでもある、という非対称な構造になります。

flowchart TB
    W1(["write (DC1)"]) --> LA["Leader A"]
    W2(["write (DC2)"]) --> LB["Leader B"]
    LA <-- "相互レプリケーション" --> LB
    LA -- log --> FA["Follower A1"]
    LB -- log --> FB["Follower B1"]

主な用途

シングルリーダーで困る場面に投入されます。

  • マルチデータセンター
    • 各データセンターにリーダーを置き、ローカルでの書き込みレイテンシを下げる
    • データセンター間の通信障害でも各拠点は独立して書き込みを続けられる
  • オフラインクライアント
    • モバイルアプリのカレンダーやノートのように、ネットワーク不通でもローカルで書き込めて後から同期する仕組み
    • 各端末を「リーダー」とみなしたマルチリーダーの一種
  • リアルタイム協調編集
    • Etherpad や Google Docs など、複数ユーザーの同時編集を扱うアプリケーション

競合解決が必須になる

マルチリーダーの宿命は書き込み競合です。同じキーへの書き込みが2つのリーダーで同時に発生したとき、どちらを正とするかを決めなければなりません。解決策は本記事の後半で扱います。

強み・弱み

  • 強み
    • 地理的に分散できる
    • 書き込みレイテンシが低い
    • データセンター障害に強い
  • 弱み
    • 競合解決が必須で運用が複雑
    • 整合性の保証が弱まる

リーダーレス方式

リーダーを持たず、クライアントが複数のレプリカに直接書き込み・読み取りを行う方式です。Amazon の Dynamo 論文(2007)に源流を持ち、Riak / Cassandra / Voldemort / ScyllaDB(Cassandra 互換)といった「Dynamo スタイル」の DB が採用しています。

仕組み

クライアントは書き込みを N 台のレプリカ(同じデータを持つノード群)に並列に送り、W 台から成功応答が返れば書き込み成立とします。読み取りも同様に R 台に並列に問い合わせ、最新と思われる値を採用します。古いレプリカは後述の仕組みで追いつかせます。

flowchart LR
    W(["write<br/>(W台に並列)"]) --> R1["Replica 1"]
    W --> R2["Replica 2"]
    W --> R3["Replica 3"]
    R1 --> Rd(["read<br/>(R台に並列)"])
    R2 --> Rd
    R3 --> Rd

Quorum(W+R>N)の考え方

レプリカ数 N、書き込み確認数 W、読み取り確認数 R に対して W + R > N を満たすと、書き込み先と読み取り先のノード集合が必ず1台以上重なるため、最新の書き込みを必ず読み取れます。これがクォーラム(Quorum、定足数の意)の考え方です。

W と R を高くするほど整合性が強くなり、低くするほど可用性とスループットが上がります。Cassandra の ONE / QUORUM / ALL のような整合性レベル設定で、操作ごとに切り替えられます。

詳細は冒頭で紹介した整合性モデルの記事で扱っているため、本記事では概念紹介に留めます。

読み取り修復と反エントロピー

リーダーレスでは古いレプリカを最新化する仕組みが必要です。

  • 読み取り修復(read repair)
    • クライアントが読み取り時に複数レプリカから値を取得し、古いレプリカを検出したら最新値を書き戻す
    • 読み取りが頻繁なキーに有効
  • 反エントロピー処理(anti-entropy)
    • バックグラウンドプロセスがレプリカ間の差分を検出し、古いレプリカを継続的に修復する
    • 読み取られないキーにも対応できる

強み・弱み

  • 強み
    • 単一障害点なし
    • ノード追加で線形スケール
    • 可用性が極めて高い
  • 弱み
    • 強整合性が出しにくい(操作単位で W/R を上げる必要がある)
    • 書き込み競合の解決責任がアプリ側に押し付けられがち

レプリケーションラグが引き起こす3つの異常

3方式に共通する難所がレプリケーションラグです。フォロワー(またはレプリカ)が最新化されるまでの時間差で、非同期レプリケーション前提のシステムでは避けられません。通常は1秒未満ですが、容量限界やネットワーク問題で数秒〜数分まで広がります。

DDIA は問題化する3つの異常を整理しています。

  • 自分の書き込みが読めない(read-after-write 違反)
    • ユーザーがプロフィール画像を更新した直後に古い画像が見える
  • 時間が逆行する(モノトニックな読み取り違反)
    • 一度新しいデータを見たユーザーが、リロードで古いデータに戻る
  • 因果関係が崩れる(一貫性のあるプレフィックス読み取り違反)
    • 質問より先に答えが見える、というように原因と結果の順序が逆転して観測される

それぞれの典型的な対策(リーダーから読む / 同じレプリカに固定する / 因果メタデータを使う)も冒頭で紹介した整合性モデルの記事で詳しく扱っています。

競合解決の戦略

マルチリーダーとリーダーレスでは、同じキーへの並行書き込みが避けられません。解決手段は大きく3系統あります。

LWW(Last Write Wins)

タイムスタンプが新しい方を勝たせる単純な方式です。Cassandra が採用しています。

実装は簡単ですが、負けた側の書き込みは黙って消えるためデータ損失のリスクがあります。物理時刻に依存するためクロックスキュー(ノード間の時刻ずれ)にも弱く、整合性を厳密に問うシステムでは推奨されません。

バージョンベクトル

各レプリカ・各キーごとにバージョン番号を持たせ、書き込みのたびにインクリメントします。クライアントは過去の読み取りで得たバージョンを書き込み時に渡し、サーバーはそれより古いバージョンの書き込みのみ上書きし、並行する書き込みは複数バージョンとして残します。

並行書き込みは「お互いを認識していない書き込み」として検出され、アプリケーションがマージ責任を負います。Riak 2.0 はドット付きバージョンベクトルを採用しています。

DDIA はショッピングカートを例に、和集合マージの落とし穴を紹介しています。片方のカートでアイテムが削除されているのに和集合を取ると復活してしまう、という Amazon の実例です。削除を表現するにはトゥームストーン(tombstone、墓石)という削除マーカーが必要になります。

CRDT・操作変換

並行書き込みを自動でマージするデータ構造・アルゴリズムです。

  • CRDT(Conflict-free Replicated Datatypes)
    • 集合・カウンター・マップなどを、どの順序でマージしても同じ結果になるよう数学的に設計したデータ構造
    • Riak 2.0 で実装された
  • 操作変換(Operational Transformation)
    • 編集操作そのものを変換しながら適用することで競合を解決する手法
    • Etherpad や Google Docs の協調編集で使われる
  • マージ可能な永続データ構造
    • Git が代表例
    • ブランチをマージするときにファイル単位で衝突解決する

これらはアプリケーション側のマージロジック実装を肩代わりしてくれますが、扱えるデータ型やマージセマンティクスに制約があるため、用途に合うかは個別判断になります。

設計判断のヒント

製品選定で「どの方式か」は概ねプロダクト選択で決まります(PostgreSQL を選べばシングルリーダー、Cassandra を選べばリーダーレス)。逆に言えば、製品選定の段階でどの方式の特性を取りに行くかを判断軸にできます。

各プロダクトのレプリケーション方式・整合性オプションの個別整理は『主要NoSQLプロダクトリファレンス: Redis・Cassandra・DynamoDB・MongoDB・Neo4j を中心に』で扱っています。マルチDC・グローバル展開などユースケースから逆引きで候補プロダクトを絞る整理は『ユースケース別NoSQL選定ガイド: 「こういうときはどのNoSQL?」を逆引きする』を参照してください。

  • データセンターは1つか複数か
    • 単一 DC ならシングルリーダーで十分
    • 複数 DC で各拠点がローカルで書きたいならマルチリーダーかリーダーレス
  • 書き込みレイテンシをどこまで許容できるか
    • ローカルで即応答したいなら同期レプリケーションは候補から外れる
  • 強整合性が必要か
    • 銀行取引・在庫管理など正確性が問われるならシングルリーダー+同期 or 準同期がデフォルト
  • 競合解決をアプリで書ける開発体制か
    • マルチリーダーやリーダーレスは競合をアプリで処理する場面が出てくる
    • CRDT で逃げ切れるか、ロジックを書き切れるかを評価する
  • オフラインクライアントの存在
    • モバイルアプリで端末側に書き込みたいならマルチリーダー的な仕組みが必須
  • 運用ノウハウ
    • チームが慣れている方式を選ぶことも判断軸
    • マルチリーダー・リーダーレスは運用難易度が一段上がる

まとめ

  • レプリケーションは障害耐性・読み取りスケール・地理的レイテンシ短縮のために使う
  • 方式はシングルリーダー / マルチリーダー / リーダーレスの3つで、どの目的を優先するかで選ぶ
  • シングルリーダーは単純で整合性が出しやすいが、リーダーが単一障害点でフェイルオーバーに罠がある
  • マルチリーダーは地理分散・オフライン・協調編集向き。書き込み競合の解決が必須
  • リーダーレスは Dynamo 系で採用され、クォーラム(W+R>N)で整合性を調整する。読み取り修復と反エントロピーで古いレプリカを追いつかせる
  • 共通課題のレプリケーションラグと競合解決は、整合性モデルとセットで設計する

3方式の比較を表にまとめます。

観点シングルリーダーマルチリーダーリーダーレス
書き込み受付リーダー1台のみ複数のリーダー任意のレプリカ
競合発生起きない起きる(解決必須)起きる(解決必須)
整合性の出しやすさ出しやすい弱いクォーラム調整で可変
フェイルオーバー必要・難所リーダーが他にもあるので柔軟不要(レプリカ間で対称)
地理分散の適性低い高い高い
主な採用例PostgreSQL、MySQL、Oracle、MongoDBCouchDB、PostgreSQL BDR、協調編集システムCassandra、Riak、Voldemort、ScyllaDB
強み単純・整合性書き込みレイテンシ・拠点独立単一障害点なし・線形スケール
弱みリーダーボトルネック・SPOF競合解決の運用負荷競合がアプリ側に染み出す

参考書

タグ: RDB , NoSQL , データベース基礎 , 分散データベース