🔁
概念 #データ設計 #レプリケーション #分散システム #一貫性 #DDIA 📚 データ志向アプリケーション設計(DDIA)

レプリケーション

同じデータを複数ノードに保持する方法。シングルリーダー・マルチリーダー・リーダーレスの3方式と、レプリケーション遅延が生む一貫性の問題を理解する

定義

レプリケーション:同じデータを、ネットワークで接続された複数のマシンに保持すること。

目的:

  • 地理的にユーザーに近いデータ(低レイテンシ)
  • 一部のノードが故障しても継続稼働(可用性)
  • 読み取りクエリを複数ノードに分散(スループット向上)

3つのレプリケーション方式

シングルリーダー(Single-Leader)
  書き込みは1ノードのみ受け付ける。最もシンプル。

マルチリーダー(Multi-Leader)
  複数ノードが書き込みを受け付ける。データセンター間に使いやすい。

リーダーレス(Leaderless / Dynamo-style)
  すべてのノードが書き込みを受け付ける。CassandraやDynamo。

シングルリーダーレプリケーション

クライアント
  │ 書き込み

リーダー(プライマリ)
  ├─── レプリケーションログ ──→ フォロワー1
  └─────────────────────────→ フォロワー2
  
読み取り: リーダー or フォロワー

同期 vs 非同期レプリケーション

方式動作保証リスク
同期フォロワー確認後に書き込み完了耐久性高いリーダーが遅いフォロワーを待つ
非同期フォロワーの確認を待たない高速フォロワーが遅れうる(Lag)
半同期1つのフォロワーだけ同期バランス型実用的な妥協点

レプリケーション遅延の問題

非同期レプリケーションでは、フォロワーが最新状態に追いついていない間に読み取りをすると古いデータが返る(最終的一貫性)。

自分の書き込みを読む(Read-your-writes)

ユーザーが自分のプロフィールを更新
  → 書き込みはリーダーへ
  → 直後に読み取りがフォロワーへ
  → 古いプロフィールが表示される(ユーザーの書き込みが「消えた」ように見える)

対策:自分のデータを読む時はリーダーから読む。最後の書き込み時刻をクライアントが持ち、それ以降の更新が来るまでリーダーに向ける。

モノトニックな読み取り(Monotonic reads)

ユーザーAがコメントを投稿
フォロワー1 → コメントあり(進んでいる)
フォロワー2 → コメントなし(遅れている)

同じユーザーBが交互にアクセスすると、コメントが「消えたり出たり」する

対策:同一ユーザーは常に同じフォロワーに向ける(ユーザーIDのハッシュで振り分け)。

マルチリーダーレプリケーション

複数のデータセンターに書き込みを受け付けるリーダーを配置。

データセンター A          データセンター B
  リーダーA  ←────────→  リーダーB
  フォロワー           フォロワー
  
ユーザーは最寄りのデータセンターに書き込む

書き込み競合(Write Conflict)

UserA が title を "A" に変更 → リーダーA
UserB が title を "B" に変更 → リーダーB(同時に)

どちらが正しいか自動では決められない。競合解決戦略

  • Last Write Wins(LWW):タイムスタンプで最後を勝者に(精度問題あり)
  • On Read:競合をすべて保存し、読み取り時にアプリが解決
  • On Write:書き込みハンドラで解決ロジックを実装
  • CRDT(Conflict-free Replicated Data Type):数学的に競合しないデータ構造

リーダーレスレプリケーション(Dynamo-style)

Amazon DynamoDBやCassandraが採用。**クォーラム(Quorum)**で一貫性を確保。

n = 総レプリカ数
w = 書き込み確認が必要なノード数
r = 読み取りで参照するノード数

w + r > n ならば、読み取りは必ず最新の書き込みを含む少なくとも1ノードを参照する

例:n=3, w=2, r=2

書き込み: 3ノードのうち2つの確認でOK
読み取り: 3ノードのうち2つを参照 → バージョン番号で最新を選ぶ

リードリペア vs アンチエントロピー

  • リードリペア:読み取り時に古いレプリカを発見したら更新
  • アンチエントロピー:バックグラウンドプロセスが差分を常に同期

クォーラムは「強い一貫性の保証」ではない。同時書き込みの競合などでセーフガードをすり抜けることがある。

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

シングルリーダーでリーダーが死んだ場合:

  1. リーダー障害の検知(タイムアウト)
  2. 新しいリーダーの選出(最新のフォロワーを昇格)
  3. クライアントの向き先変更

問題

  • 非同期レプリカが選ばれると、古いリーダーが持っていた書き込みが失われる
  • スプリットブレイン:2ノードが同時にリーダーと思い込む
  • タイムアウト設定が難しい(短すぎると誤検知、長すぎると回復が遅い)

関連概念

出典・参考文献

  • Martin Kleppmann, Designing Data-Intensive Applications (2017) Chapter 5
  • DeCandia et al., “Dynamo: Amazon’s Highly Available Key-value Store” (2007)

出典: Martin Kleppmann, 'Designing Data-Intensive Applications' (2017) Chapter 5