このページの目次

複製戦略

複数のマシンが同じデータの複製を保持する——どのようにしてそれらの整合性を保証するか? シングルリーダーは単純だが書き込みのボトルネックとなり、マルチリーダーは遠隔地での書き込みを許可するが競合が多発し、リーダーレスは一貫性を犠牲にして可用性を獲得する。一貫性、レイテンシ、耐障害性の間のトレードオフは、分散ストレージにおける最初の分岐点である。

シングルリーダー (Primary-Backup / Master-Slave)

すべての書き込み操作はリーダーを経由しなければならない。リーダーは書き込みを自身の WAL (Write-Ahead Log) に永続化し、その後、レプリケーション(同期または非同期)によってフォロワーへ伝播させる。

Client → Leader:   write(x=1)
Leader:             WAL への追加 → fsync → クライアントへ OK を返す (同期の場合)
                    → フォロワーへの非同期または同期レプリケーション
Followers:          new entries を受信 → ローカル状態に適用
Client → Follower: read(x) → 古い値を返す可能性がある (レプリケーション遅延のため)

複製方式

同期複製⁠:リーダーは少なくとも 1 つのフォロワーが確認を行うまでクライアントへの応答を待機する。これにより、少なくとも 1 つのフォロワーに最新データが存在することが保証され、リーダーがクラッシュした場合でも、そのフォロワーが引き継いでデータ損失を防ぐことができる。代償として、書き込みレイテンシは 2 ノード間の最も遅い fsync 時間になる。

非同期複製⁠:リーダーはフォロワーの確認を待たない。書き込みのレイテンシは最小限だが、リーダーがクラッシュした場合、未確認の書き込みは失われる (RPO > 0)。

準同期 (MySQL semisync):少なくとも 1 つのフォロワーの確認を待つが、すべてのフォロワーを待たない。永続性とレイテンシのバランスを取る。

Read-Your-Writes 問題

非同期複製の場合、クライアントがリーダーへの書き込み直後にフォロワーから読み込むと、直前に書き込んだデータが読み取れない可能性がある。解決策:1) 書き込みに関連する因果関係を持つ後続の読み込みに対してリーダーへの読み込みを強制する; 2) フォロワーが現在適用されているログの位置を返し、クライアントは次の読み込みでトークンを指定する → フォロワーはトークン >= になるまで待機する。

マルチリーダー (Multi-Master)

すべてのノードが書き込みリクエストを受け付けることができ、他のノードへは非同期で複製される。利点:書き込みレイテンシが低い(リーダーを経由しない)、ネットワーク分断時に書き込みが可能(各分断内のノードが独立して書き込みを受け付ける)。代償:書き込み競合。

Node A: write(x=1) → 非同期複製 → Node B
Node B: write(x=2) → 非同期複製 → Node A
→ 競合発生! A は (x=1 → x=2) を確認、B は (x=2 → x=1) を確認
→ 競合解決が必要 (LWW, バージョンベクトル, CRDT — 03-02 を参照)

リーダーレス (Dynamo-style)

リーダーの役割がない——書き込みは複数のノードへ、読み込みも複数のノードから行われ、クォーラムによって整合性が保証される。

Dynamo (Amazon 2007) のモデル:

  • N: データが複製されるノードの総数
  • W: 書き込みの承認に必要なノード数 (コーディネーターを含む)
  • R: 読み込みに関与するノード数 (コーディネーターを含む)
  • R + W > N の場合: 読み書きのクォーラムには必ず重複が生じる → 少なくとも 1 つのノードに最新データが存在し、線形整合性が保証される
コーディネーター (任意——クライアントは直接、またはロードバランサー経由で任意のノードへアクセス可能):
  write(key, value, context):
    1. ベクトルクロックバージョンを生成
    2. N 個のレプリカへ並列送信
    3. W-1 個の確認を待機 (自身を含めて W 個)
    4. クライアントへ OK を返す

  read(key):
    1. N 個のレプリカから並列で読み込み (リクエスト送信)
    2. R 個の応答を待機
    3. ベクトルクロック比較により、バージョンが最新の value を選択
    4. 競合が見つかった場合 (バージョンが比較不可能) → 競合しているすべての sibling をクライアントへ返す (アプリケーション層で解決)
    5. 非同期修復 (read repair): コーディネーターが最新バージョンを遅れているノードへ書き戻す

Hinted Handoff

コーディネーターがすべての N 個のレプリカに到達できない場合、データをコーディネーター自身のローカルの "hint" に一時的に保存する。ターゲットレプリカが回復した際、コーディネーターは hint を転送する。これにより、ノード障害時にシステムが引き続き利用可能になる。

Sloppy Quorum vs Strict Quorum

厳密なクォーラムは、少なくとも W 個の "ターゲットレプリカ" への書き込みを要求する。Sloppy quorum: 合計で W 個のノードが確認を行えば(ターゲット以外のノードが引き受けても可)、可用性が確保される——ノード障害時に可用性が向上するが、R+W>N による整合性の保証が違反される可能性がある。

参考

  • 論文⁠: "Dynamo: Amazon's Highly Available Key-value Store" (2007)
  • Cassandra: cassandra.apache.org (Dynamo と BigTable のハイブリッド)
  • MySQL: Group Replication (Paxos ベース、シングルリーダーの派生)

Keywords: リーダーベースのレプリケーション, マルチリーダー, リーダーレス, クォーラム, Dynamo, ヒントドハンドオフ, リードリペア, スロークォーラム