コンピューターサイエンス:アルゴリズム / OS / ネットワーク / DB
結果整合性
結果整合性
このレッスンで分かること
- 結果整合性 (Eventual Consistency) と強整合性の違い
- BASE という設計思想
- 結果整合性で起きる現象とアプリ側の対処
結果整合性 とは
BASE と AP 系システムが選ぶ妥協と利点。本レッスンでは、結果整合性 の基本から実際の使いどころまでを整理し、現場で迷わず使える形に落とし込みます。
強整合性 vs 結果整合性
強整合性 (Strong Consistency) とは「書き込み完了後、どのノードから読んでも必ず最新値が返る」状態です。RDB のトランザクションが提供するのはこれです。
結果整合性 (Eventual Consistency) は「最終的には 全ノードが同じ値に収束する。途中は古い値が見えてもよい」状態です。AP 系の分散システム(Cassandra、DynamoDB、Redis Cluster、CDN など)が採用します。
なぜ結果整合性で OK な場面があるか
「整合性が緩いなんて怖い」と思うかもしれません。しかし、現実のシステムには 少し古いデータが見えても困らない ユースケースが多くあります。
- SNS のいいね数 — 一瞬古くても、最終的に正しい数字に収束すれば問題ない
- タイムライン — 投稿が数秒遅れて見えても許容範囲
- 検索インデックス — 投稿から検索可能になるまで数秒遅延しても OK
- CDN キャッシュ — エッジで古いコンテンツが配信されることを前提
- DNS — TTL ぶん遅延する前提で設計
逆に金融や在庫など「今すぐ正しくないと困る」場面では強整合性が必須です。
BASE という思想
ACID に対する対称概念が BASE です。
- Basically Available — 基本的に使える(一部不調でも全体は止めない)
- Soft state — 状態は変動しうる(古いデータが見えるかも)
- Eventually consistent — 最終的には整合する
BASE は「整合性を一時的に諦めて可用性とスケーラビリティを取る」哲学です。
ACID は「絶対に間違えない」を目指す。BASE は「最終的に正しければ良い」を許容する。
結果整合性で発生する 4 つの現象
1. Stale Read(古い読み取り)
書き込み直後に別ノードから読むと、古い値が返ります。前のレッスンの「レプリカラグ」と同じ現象です。
2. Read Your Writes の崩壊
「自分が書いた直後に自分で読むと、自分の書き込みが反映されていない」現象。ユーザー体験が破壊されるので、最重要の対策ポイント です。
プレーンテキスト
T1: POST /comments → Node A に書き込み
T2: GET /comments → Node B から読む → 自分のコメントが見えない対策は次の通りです。
- 直近の書き込みは同じノードから読む(Sticky Session)
- セッションごとに「書き込み時刻」を保持し、それ以降を読む(Bounded Staleness)
- 書き込み直後の読み取りは Primary に向ける
3. Monotonic Read の崩壊
「2 回連続で読んで、2 回目が 1 回目より古くなる」現象。Node B が新しい値、Node C が古い値を持っている状態で、ロードバランサがランダムに振ると起きます。
対策はセッション単位で同じノードに固定すること。
4. 書き込み衝突
2 つのノードで同時に同じキーを更新したとき、どちらが勝つかを決める必要があります。代表的な解決方式は次のものです。
- Last-Write-Wins (LWW) — タイムスタンプが新しい方を採用。シンプルだが時計ずれに弱い
- Vector Clock — 因果関係を追跡してマージ
- CRDT (Conflict-free Replicated Data Types) — 衝突しないデータ構造を使う
- アプリ側マージ — クライアントに衝突を見せて選ばせる(Git のコンフリクト解決と同じ)
クォーラムによる強整合性の取り戻し
結果整合性 DB でも、クォーラムを設定すれば擬似的に強整合性を実現できます。
- N: 全ノード数
- W: 書き込み成功に必要なノード数
- R: 読み取りで参照するノード数
- W + R > N なら強整合性
例えば 5 ノードで W=3, R=3 なら、書き込みと読み取りに必ず 1 つは共通ノードがあり、最新値を読めます。
| W と R の関係 | 整合性 | 遅延 |
|---|---|---|
| W + R > N | 強整合性 | 最も遅い |
| W + R = N | グレーゾーン | 中 |
| W + R < N | 結果整合性 | 速い |
CRDT という解決策
衝突しないデータ構造の CRDT (Conflict-free Replicated Data Type) は、複数ノードで同時編集してもマージできる性質を持ちます。
- G-Counter — 加算のみ可能なカウンター。各ノードが自分の増分を持ち、合計値で全体を表現
- G-Set — 追加のみのセット
- OR-Set — 追加と削除が可能、削除も「削除タグ」として記録
- LWW-Register — タイムスタンプ付き値、最新が勝つ
Google Docs や Figma のリアルタイム共同編集は CRDT 系の技術で動いています。
ベクトル時計で因果を追う
ノードごとにカウンタを持ち、書き込みのたびに自分のカウンタを増やす ベクトル時計 で、書き込みの因果関係を追跡できます。
プレーンテキスト
Node A: [A=3, B=1, C=2]
Node B: [A=2, B=5, C=2]A=3 vs A=2 で A は前者が新しい、B=1 vs B=5 で B は後者が新しい。両方を持つ並行イベントなので、マージ戦略が必要、と判断できます。
アプリ設計の指針
結果整合性 DB を使うときは次を意識しましょう。
- 書き込み直後の読み取りパスをどう守るか決める(Sticky Session、Primary 読み)
- 衝突解決ルールをドキュメント化する
- ユーザーに古い値が見えうることを UI で示唆する(「最終更新: 2 秒前」など)
- 集計値はクライアントで保存しない(サーバから取り直す)
結果整合性は「事故ではなく仕様」。仕様として設計すれば怖くない。
やってみよう
- DynamoDB で
ConsistentRead=false(デフォルト) とtrueで読み取り時間を比較する - Redis Cluster で複数ノードに書き込み、別ノードから読んで遅延を測る
- 自分のプロダクトで「結果整合性で問題ない機能」「許されない機能」を分類する
よくある質問
Q. このトピックは実務でどう役立ちますか?
A. DB のクエリ最適化、API 設計、データ構造の選択など、設計判断の根拠になります。表面的にライブラリを使うだけでなく「なぜそれが速いのか」を理解できると、性能問題を未然に防げます。コーディング面接でも頻出のテーマです。
Q. 結果整合性でデータが失われることはありますか?
A. 適切に設計されていれば失われません。ノード間の伝播が「遅れる」だけで、最終的には全ノードが同じ値に収束します。ただし LWW(Last-Write-Wins)を使う場合は、タイムスタンプのずれで後から届いた古い書き込みが上書き勝ちするリスクがあります。CRDT やベクトル時計を使うと因果関係を保持したままマージできます。
Q. クォーラムを設定すれば必ず強整合性になりますか?
A. W + R > N を満たすクォーラムを設定すると「書き込みと読み取りに必ず 1 つ以上の共通ノードが含まれる」ため、擬似的な強整合性を得られます。ただしノード障害でクォーラムが満たせなくなった場合の挙動や、ネットワーク分断時の動作はシステムの設定に依存します。
Q. LWW と CRDT はどう使い分けますか?
A. LWW はシンプルで実装コストが低く、「最後の更新だけが意味を持つ」ユースケース(セッション情報や設定値など)に向いています。CRDT はカウンター・セット・テキスト共同編集のように「複数の書き込みをすべてマージしたい」場面で使います。Google Docs や Figma のリアルタイム編集が CRDT 系の技術を採用しているのはその理由からです。
次のレッスン
次は NewSQL と分散 SQL で、強整合性とスケーラビリティを両立させる NewSQL の仕組みと、従来の分散 DB との違いを学びます。
事前確認 — 進む前に次の 3 つができることを確認しましょう。
- 結果整合性 の要点を自分の言葉で説明できる
- このレッスンの最小コード (または操作手順) を見ずに書ける
- 練習問題やクイズで間違えた箇所を読み直して理解した
理解度チェック (30 秒)
Q. 結果整合性 とは何か、1 文で説明してください。
A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。
関連レッスン