🚀

【データ指向アプリケーション要約】 第9章 一貫性と合意

2025/01/03に公開

「第9章 一貫性と合意」は、分散システムでよく出てくる 「データの正しさ(一貫性)」「みんなで同じ決定をする(合意)」 について、濃い内容が詰まった章だった。
だいぶ良かった。(薄い)
要点を押さえながらまとめていく。

1. 一貫性の保証

そもそも一貫性って何?

  • 分散システムでは、データがいろんなマシンにコピーされるじゃない?
    そのとき「どのマシンを見ても同じ内容が見えるの?」とか「書き込んだはずのデータが、すぐに反映されるの?」みたいな話が出てくる。
  • “一貫性”とはつまり「データの見え方がどこまで正しいか」を示すレベルのことだね。

いろんなレベルがある

  • 強い一貫性(強整合性): 常に最新状態が全ノードで同じに見える、みたいな理想形。
    でも実現コストが高い。
  • 最終的に一貫性(Eventual Consistency): 一時的にズレはあるけど、時間が経てば落ち着くよ、的なゆるい整合性。
    ほかにも「read-after-write 一貫性」や「モノトニックリード」など、いろんなモデルがあったよね。

9章ではさらに踏み込んで、線形化可能性(linearizability) っていう “めちゃ厳密なモデル” を中心に話が進んでく。

2. 線形化可能性(Linearizability)

「線形化可能性」って聞き慣れない言葉やけど、「どんなに分散してても、まるでひとつのデータベースが瞬時に更新されたように見せる」 という強い一貫性の理論モデルだと思っていい。

2.1 システムを線形化可能にする条件は?

  • クライアントがあるオペレーション(読み書き)をしたら、「その時点以降の全ての読み取りは、その結果を反映してる」
  • 分散環境で実現しようとすると、ネットワーク遅延や障害を考えてかなり大変
  • 実装例:
    • シングルリーダーで、リーダーが書き込みを厳密に順序付けして、読み取りは必ずその順序を守るとか
    • アトミックな操作(たとえばCompare-and-Setみたいな操作)がちゃんと1回ずつ直列に処理されることを保証するなど

2.2 線形化可能性への依存

  • アプリのロジック上、「絶対に古いデータを読んではまずい」ケースや、「Compare-and-Setで同時更新が起きないように制御したい」みたいなときに有効
  • ロックや分散ロックを使うときも、内部では線形化を担保するアルゴリズムが動いてたりする

2.3 線形化可能なシステムの実装

  • 一番シンプルなのは「リーダーが全部仕切って、書き込みを直列化する」パターン
  • ただし高負荷・高遅延になるリスクがあって、スケーラビリティや可用性とのトレードオフがある

2.4 線形化可能にすることによるコスト

  • ネットワーク往復が増えたり、ノード障害時に書き込みが止まったり、、、
  • 強い保証を得る分、性能の犠牲可用性の犠牲 などのコストがかかる

3. 順序の保証

線形化可能性は「システム全体で1つのタイムラインがある」ってイメージやけど、現実の分散システムでは “部分的な順序” しか保証できないケースも多い

3.1 順序と因果関係

  • 因果関係: 「Aが起きたからBが起きた」という関係。チャットで「質問」があって「回答」がある、みたいな
  • 分散環境だと、AとBのイベントが同時っぽく起きた場合、どっちが先か曖昧になることがある
  • “因果一貫性”みたいに、「因果関係のあるイベントは順序を守る」けど、無関係なイベントは順序を厳密にしなくてもOK、という緩やかなモデルもある

3.2 シーケンス番号の順序

  • 全てのイベントにシーケンス番号を振って順序づける方法
  • ただし分散環境で「絶対一意かつ単調増加な番号」を振るには、やっぱり中央集権的な仕組み(リーダーなど)が必要だったりする

3.3 全順序ブロードキャスト

  • “全ノードが同じ順序でメッセージを受け取る”ことを保証する仕組み
  • リーダーがメッセージの順番を決めてから各ノードに送る、みたいな実装が代表例
  • これも 実装が大変 だし、リーダーが故障したらどうするかなど問題が出てくる

4. 分散トランザクションと合意

4.1 アトミックなコミットと2相コミット(2PC)

  • 2相コミット(2-Phase Commit) は、分散トランザクションでおなじみのプロトコル
    • まず「みんなコミットOK?」って尋ねて(prepareフェーズ)、全員OKなら「よし、コミットだ!」と確定する(commitフェーズ)
    • 誰かがNGを出したら全体をロールバックする
  • ただし、2PCはフェイルオーバーに弱い とか、コーディネータ(調整役)が落ちると止まってしまう…といった弱点もある

4.2 分散トランザクションの実際

  • 「分散ACIDトランザクション」は便利だけど、システムが大規模になればなるほど遅延や障害の影響が大きい
  • なので最近の大規模分散システムでは、「なるべく分散トランザクションを減らす」設計(マイクロサービスっぽい発想)が多い

4.3 耐障害性を持つ合意

  • PaxosRaft といった合意アルゴリズムが有名
  • 複数ノードで「この値を正としよう!」と決めるときに、少数のノード障害やネットワーク障害があっても合意できる仕組み
  • この合意アルゴリズムを使ってリーダー選出や設定変更を安全に行うことが多い

4.4 メンバーシップと協調サービス

  • 分散システムで「今、どのノードが参加してるの?」とか、設定ファイルなどのグローバル情報を安全に共有するために、Zookeeperetcd みたいな「協調サービス」(コーディネーションサービス)がよく使われる
  • これらのサービスの中核にも、Paxos/RAFTみたいな合意アルゴリズムが入ってる

まとめ

  • 一貫性(Consistency): 分散システムで「どこまで正しく最新状態を見せるか」をどう保証するか
    • 特に「線形化可能性(Linearizability)」は最強レベルの一貫性モデルだけど、コストも高い
  • 順序(Ordering): イベントの因果関係や全順序をどう保つか
    • 全順序ブロードキャストは便利だけど、実装も運用も大変
  • 合意(Consensus): 分散システムが「この値」「この状態」で一致しよう、と決める仕組み
    • 2PCやPaxos、Raftなど。トランザクションのコミットやリーダーの選出に使われる
  • 線形化可能性や合意を強くすると、どうしても性能や可用性が下がる
  • だから分散アプリケーションは、どの部分に強い一貫性が必要か、どの部分は多少緩くてもOKかを見極めるのが大事

Discussion