Skip to content

필사 모드: 分散システム完全ガイド — 時計・Consensus・Event Sourcing・Saga・CRDT・障害パターン (Season 2 Ep 12, 2025)

日本語
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.

はじめに — 分散システムが難しい本当の理由

Butler Lampsonの有名な定義:

「分散システムとは、存在すら知らなかったコンピュータの故障が、自分のコンピュータを使えなくするものである。」

分散システムが難しいのは、次の3つが同時に起こるからだ:

  1. 時計が違う (Clock Drift)
  2. 故障が部分的である (Partial Failure)
  3. ネットワークは信用してはいけない (Unreliable Network)

本稿はこの3つの問題に対する理論的な道具(時計・合意・一貫性)と実戦のパターン(Event Sourcing・Saga・CRDT)を一度に整理する。


第1部 — 8 Fallacies of Distributed Computing

Peter Deutschが1994年にまとめた分散システムの8つの誤った仮定:

  1. ネットワークは信頼できる
  2. 遅延はゼロである
  3. 帯域は無限である
  4. ネットワークは安全である
  5. トポロジは変わらない
  6. 管理者は一人である
  7. 転送コストはゼロである
  8. ネットワークは均質である

この8つに反するすべての仮定は、いつか本番で爆発する。


第2部 — 時間の3つのモデル

2.1 Physical Clock (物理時計)

time.Now() — OSの時計。NTPで同期する。

問題:

  • Clock Drift (ppm単位の誤差)
  • NTPのジャンプ (時刻調整で飛び越える)
  • Leap Second
  • データセンタ間でms〜数十msの誤差

2.2 Logical Clock (論理時計)

Lamport Timestamp (1978):

イベント発生時に counter++
メッセージ送信: (event, counter) を送る
メッセージ受信: counter = max(local, received) + 1

メリット: 因果関係の保存 (happens-before) デメリット: 因果関係を持っていたのかどうかを区別できない (concurrent vs causal)

2.3 Vector Clock

ノード数Nと同じ長さのベクトル:

A: [2, 1, 0]
B: [1, 3, 0]
  • AとBがconcurrentなのか、どちらが先なのかを判別できる
  • サイズの問題: ノード数が多いとメタデータが大きくなる

2.4 HLC (Hybrid Logical Clock, 2014)

物理時間 + 論理カウンタ の組み合わせ:

HLC = (physical_time, logical_counter)
  • 物理時計を基準にした整列 + 論理時計による因果の保存
  • CockroachDB、YugabyteDBなど現代の分散DBの標準

2.5 TrueTime (Google Spanner)

GPS・原子時計で ±7ms を保証 → 「この時点より後のイベントは確実に後ろ」が可能になる。

  • Commit Wait: 不確実性の区間(ms)だけ待ってから完了する
  • 強い一貫性 + 性能 (Linearizable)
  • 一般企業には実現不可能 (ハードウェア投資)

第3部 — 一貫性モデルのスペクトラム

3.1 主要な一貫性モデル

強い → 弱い順:

  1. Linearizable: すべての操作が実時間の順序どおりに見える
  2. Sequential: すべてのプロセスが同じ順序で見るが、実時間ではない
  3. Causal: 因果関係のあるものだけ順序が保証される
  4. Eventual: 結局は同じになる

3.2 CAPの再訪

  • Consistency: Linearizability
  • Availability: すべてのリクエストが答えを受け取る
  • Partition Tolerance: ネットワーク分断時にも動作する

ネットワーク分断は避けられない → CPかAPかを選ぶ。

3.3 PACELC (より現実的)

  • Partition: C or A
  • Else (正常運用時): L(atency) or C(onsistency)

ほとんどのシステムはPA/EL (分断時は可用性 + 正常時は低遅延)。

3.4 2025のDB一貫性の選択

DBモデル
PostgreSQL単一ノードのSerializable
MySQLRead Committed (デフォルト)
SpannerLinearizable + External Consistency
CockroachDBSerializable + Causal
CassandraTunable (Quorum・Eventual)
DynamoDBEventual (オプションでStrong)
Redis Cluster単一キーはLinearizable、複数キーは ❌

第4部 — Consensus: 合意アルゴリズム

4.1 なぜConsensusなのか

分散した複数のノードが同じ値に同意する — リーダー選出、状態複製、トランザクションのコミットなど。

4.2 FLP Impossibility (1985)

非同期システムにおいて、1ノードの故障すら許容するdeterministic consensusは不可能。

実際のシステムはタイミングの仮定 (eventually synchronous) または確率的な方法を使う。

4.3 Paxos (1989)

Leslie Lamportが作った元祖。正確だが理解も実装も難しいことで悪名高い。

役割:

  • Proposer (値を提案)
  • Acceptor (投票)
  • Learner (決定を学習)

問題: 実戦では使いにくく、Multi-Paxos、Fast Paxos、EPaxosなどの変種が生まれた。

4.4 Raft (2014)

Stanfordで「理解可能なConsensus」を目標に設計された。現在は事実上の標準。

3つの核心:

  1. Leader Election: リーダーを一人選ぶ
  2. Log Replication: リーダーがFollowerにログを複製する
  3. Safety: Committedなログは覆らない

状態:

  • Follower → Candidate (Election Timeout) → Leader (過半数の投票)
  • Leaderがheartbeatに失敗したら → 新しい選出

使用先: etcd, Consul, TiKV, CockroachDB, Kafka KRaft (2024〜)。

4.5 PBFT (Practical Byzantine Fault Tolerance, 1999)

悪意あるノードが存在する状況で3f+1ノードのうちf個の故障を許容する。ブロックチェーンで使われる。

段階: Pre-prepare → Prepare → Commit。メッセージ複雑度 O(N²)。

4.6 Tendermint・HotStuff (2018〜)

BFTをブロックチェーン向けに現代化したもの。Libra/DiemのHotStuff、CosmosのTendermint。

4.7 Nakamoto Consensus

BitcoinのProof-of-Work。確率的な合意 — 十分に多くのブロックが後ろに積まれればconfirmed。

欠点: エネルギー、遅さ。だからEthereumはPoSへ移行した。


第5部 — Replication

5.1 複製パターン4つ

  1. Single-Leader: 書き込みはリーダー、読み取りはどこでも。単純。ほとんどのRDBMS。
  2. Multi-Leader: 複数のリーダーが書き込みを受ける。衝突解決が必要。まれにしか使わない。
  3. Leaderless: すべてのノードが対等。Dynamo-style。Cassandra, Riak, DynamoDB。
  4. Quorum-based: R + W > N なら強い一貫性。

5.2 Replication Lag

Leader → Followerの伝播遅延が次の問題を生む:

  • Read-your-writes: たった今書いたものが読めない
  • Monotonic reads: 以前に見えていたものが消える
  • Consistent Prefix: 原因のない結果を見る

解決: Leaderから読む、Sticky Session、Timestampベースの一貫性。

5.3 Conflict Resolution (Multi-Leader/Leaderless)

  • LWW (Last Writer Wins): 単純、データ損失の危険
  • Version Vectors: 正確、複雑
  • Application-level Merge: ユーザー定義 (CRDT)

第6部 — CRDT (Conflict-free Replicated Data Type)

6.1 CRDTとは

数学的に衝突が不可能なデータ構造。どの順序で合わせても同じ結果になる。

6.2 2つの型

State-based (CvRDT): 状態そのものを転送 + merge Operation-based (CmRDT): 演算を転送 + 再適用

6.3 代表的なCRDT

CRDT用途
G-Counter増加のみ (カウンタ)
PN-Counter増減
G-Set追加のみ
OR-Set追加・削除
LWW-Register最後の書き込みが勝つ
RGA順序のあるリスト (テキスト編集)
JSON CRDT (Automerge)入れ子構造

6.4 実戦での使用先

  • Redis CRDT (Enterprise): Active-Active Geo
  • Riak: Dynamo + CRDT
  • Automerge (Ink & Switch): 協働エディタ
  • Yjs: Real-time collaboration (Notion, Linearの着想元)
  • Figma: デザイン協働のための独自CRDT

6.5 限界

  • 任意の不変条件を表現しにくい (例: 一意性)
  • メタデータのサイズ (Tombstone)
  • 操作順序の意味を失う (これが利点でもあり欠点でもある)

第7部 — Event Sourcing + CQRS

7.1 Event Sourcing

状態の代わりにイベントを保存する。状態はイベントの再生から導出する。

events = [
  AccountCreated(id=1, balance=0),
  MoneyDeposited(account=1, amount=100),
  MoneyWithdrawn(account=1, amount=30),
]

balance = fold(events, 0, apply) // 70

メリット:

  • 完全な履歴 (監査・デバッグ)
  • タイムトラベル
  • イベントの再生で新しいViewを作れる
  • 分散に向く (イベントはappend-only)

デメリット:

  • 複雑度 ↑
  • スキーマ進化の問題 (古いイベントの解釈)
  • Snapshotがないと再生が遅い

7.2 CQRS (Command Query Responsibility Segregation)

書き込みモデル(Command) ≠ 読み取りモデル(Query) の分離。

Write: events → EventStore
Projection
Read: Materialized View (最適化)

メリット: 読み取りと書き込みを独立に最適化・スケールできる。 デメリット: 一貫性の遅れ (Eventual)。

7.3 Event Sourcing + CQRSのスタック

  • EventStoreDB: 専用DB
  • Kafka + Kafka Streams: イベントログ + Projection
  • Axon Framework (Java): 統合型
  • MartenDB (.NET): Postgresの上に実装

7.4 いつ使い、いつ避けるか

良いとき:

  • 金融・取引のように監査可能性が重要なとき
  • 複雑なビジネスロジック + 過去の再構成
  • イベント中心のアーキテクチャ

避けるとき:

  • 単純なCRUD
  • チームの多くが初心者
  • 一貫性の要求が単純

第8部 — Sagaパターン

8.1 分散トランザクションの問題

2PC (Two-Phase Commit): 完璧だがblockし、Coordinatorの失敗に弱い。現代ではほとんど使われない。

代替: Saga — ローカルトランザクションの連続 + 失敗時は補償トランザクション。

8.2 Choreography (振り付け)

各サービスがイベントを発行 + 購読する:

OrderCreatedInventoryReservedPaymentChargedOrderConfirmed
                  ↓ 失敗
              InventoryReleasedOrderCancelled (補償)

メリット: サービス間の結合度が低い。 デメリット: 流れの追跡が難しい。

8.3 Orchestration (指揮)

中央のOrchestratorが明示的に流れを管理する:

Orchestrator:
  1. ReserveInventory
  2. ChargePayment
  3. ShipOrder
  失敗時 ← CompensateAll

メリット: 流れが明示的で、デバッグしやすい。 デメリット: Orchestratorが単一障害点になりうる。

8.4 実装ツール

  • Temporal: ワークフローエンジン。Sagaの事実上の標準。
  • AWS Step Functions: マネージド
  • Camunda: BPMNベース
  • Cadence (Uber、Temporalの前身): オープンソース

8.5 Temporalの例

func ProcessOrder(ctx workflow.Context, order Order) error {
    err := workflow.ExecuteActivity(ctx, ReserveInventory, order).Get(ctx, nil)
    if err != nil { return err }
    
    err = workflow.ExecuteActivity(ctx, ChargePayment, order).Get(ctx, nil)
    if err != nil {
        workflow.ExecuteActivity(ctx, ReleaseInventory, order)
        return err
    }
    // ...
}

Temporalが再起動安全性・タイマー・可視化を無料で提供してくれる。


第9部 — 障害パターン12

9.1 障害の伝播パターン

  1. Cascading Failure: あるサービスが遅くなる → 呼び出し側のタイムアウトが積み上がる → 伝播する
  2. Thundering Herd: キャッシュ失効時にすべてのリクエストがOriginへ殺到する
  3. Retry Storm: 失敗したリクエストがリトライで増幅する
  4. Metastable Failure: 一時的な問題が正常復帰の後も持続する
  5. Split-Brain: ネットワーク分断で二人のリーダーができる
  6. Clock Skew Bug: 時計のずれでタイムスタンプが逆転する
  7. Gray Failure: 死んではいないが機能が劣化している
  8. Poison Pill: 特定のメッセージがすべてのConsumerを落とす
  9. Queue Backlog: Consumerが遅く、無限に積み上がる
  10. Hot Partition: 特定のシャードにトラフィックが集中する
  11. Fan-out Overload: 1件のリクエストが100件の内部リクエストになる
  12. Noisy Neighbor: 共有資源で1テナントが全体に影響する

9.2 防御パターン

障害防御
CascadingCircuit Breaker, Bulkhead, Timeout
Thundering HerdRequest Coalescing, Jittered refresh
Retry StormExponential Backoff + Jitter
MetastableLoad Shedding, Queue Depthの制限
Split-BrainFencing Token, Quorum
Clock SkewHLC, NTPの監視
Gray FailureHealth Check + 積極的な切り離し
Poison PillDead Letter Queue + Alert
Queue BacklogBackpressure, Auto-scale
Hot PartitionConsistent Hashing + replication
Fan-outTimeoutの深さを考慮、Async
Noisy NeighborResource Quota, QoS

9.3 Chaos Engineering

意図的に障害を注入する → システムを検証する。

  • Chaos Monkey (Netflix 2011): インスタンスをランダムにkill
  • Chaos Mesh: K8sネイティブ
  • LitmusChaos: オープンソース
  • Gremlin: 商用

原則:

  • 本番で (最初はステージングで)
  • Blast Radiusを制限する
  • 自動ロールバック
  • 定期的に実行する (Game Day)

第10部 — 分散システムのパターン10

  1. Idempotency: 同じリクエストをN回 = 1回の効果
  2. Exactly-once is a lie: At-least-once + Idempotent Consumer
  3. Outbox Pattern: DBトランザクションとイベント発行の原子性
  4. Inbox Pattern: 冪等なConsumerの実装方法
  5. Circuit Breaker: 失敗の検知 + 回避
  6. Bulkhead: スレッドプールの隔離で連鎖失敗を防ぐ
  7. Leader Election: Leaseベース
  8. Sharding: データの分割
  9. Sidecar: 共通機能 (ロギング、セキュリティ) を別コンテナに
  10. Service Mesh: Sidecarをネットワークレイヤへ

第11部 — 分散システムのロードマップ6か月

Month 1: 理論の基礎

  • DDIA (Designing Data-Intensive Applications) を精読
  • Lamport Paper 2編
  • 8 Fallaciesの思考実験

Month 2: Consensusの実習

  • Raftを自分で実装 (MIT 6.824)
  • etcd・Consulの内部を読む
  • Kafka KRaftの構造を理解する

Month 3: Replication + Consistency

  • 一貫性モデルを区別する練習
  • CockroachDB・Spannerの白書
  • TrueTime・HLCの実装

Month 4: Event Sourcing + Saga

  • ミニ銀行システムをEvent Sourcingで
  • TemporalでSagaを実装
  • Outboxパターンを実際に適用

Month 5: CRDT + リアルタイム

  • Automerge・Yjsの学習
  • 協働エディタのプロトタイプ
  • Figmaの技術ブログを精読

Month 6: 障害 + 運用

  • Chaos Monkeyの導入
  • 主要な障害パターンの防御を実装
  • Incident Postmortemの分析10件

第12部 — 分散システムチェックリスト12

  1. 8 Fallacies を言える
  2. Lamport vs Vector vs HLC の違いを知っている
  3. CAP vs PACELC の違いを知っている
  4. Raftの3つの核心 を説明できる
  5. FLP Impossibility の意味を知っている
  6. Single-Leader vs Leaderless のトレードオフを知っている
  7. CRDTが解決する問題 を知っている
  8. Event Sourcing + CQRS の長所と短所を知っている
  9. Saga Choreography vs Orchestration の違いを知っている
  10. Cascading Failureの防御3つ を言える
  11. Outbox・Inboxパターン の役割を知っている
  12. Chaos Engineeringの原則 を知っている

第13部 — 分散システムのアンチパターン10

  1. 「ネットワークは信頼できる」: 8 Fallaciesの1番。Timeout・リトライは必須
  2. 2PCをマイクロサービスに: Blockの危険。Sagaで
  3. イベント順序をKafkaのパーティション間で仮定する: パーティション単位でしか保証されない
  4. Exactly-onceをDBトランザクションなしで: 不可能。Outboxまたは冪等性で
  5. Clockを信頼する: time.Now() で並べる → バグ。論理時計が必須
  6. 無限リトライ: Retry Stormを誘発する。Backoff + Circuit Breaker
  7. 単一ノードDB + 複製だけで安全だと仮定: Split-Brainの考慮が必須
  8. Consumer失敗時にNACKせずブロック: DLQの設計が必須
  9. グローバルトランザクション: 性能・可用性 ↓。境界を再設計する
  10. 「Eventual Consistencyは結局同じになる」: ユーザー視点では問題。UXで解決する

おわりに — 分散システムは「心のモデル」だ

分散システムは直観が最も頻繁に裏切る領域だ。「直観が強力な」エンジニアが最も危ない。

必要なもの:

  • 疑う心: ネットワーク・時計・ノードのすべてを疑う
  • 補償する心: 原子性が無理なら補償を設計する
  • 再現する心: イベントとして記録する → いつでも再現できる

2025年のシニアエンジニアの分散の力量は年収を2倍に広げる軸だ。この領域はそれだけ難しく、だからこそそれだけ価値がある。


次回予告 — 「DB完全ガイド: 内部構造・インデックス・クエリプランナ・パーティショニング・Vector DB」

Season 2 Ep 13はデータの心臓、DB深掘り。次回は:

  • B-Tree・LSM-Tree・Hash Indexの内部構造
  • クエリプランナとEXPLAINの深い読み方
  • トランザクション分離レベル (Read Uncommitted 〜 Serializable)
  • シャーディング・パーティショニング戦略
  • PostgreSQLの独走 (2025)
  • Vector DBとpgvector・Qdrant・Weaviate

DBがどうやってクエリを処理するのかを、次回に。

현재 단락 (1/284)

Butler Lampsonの有名な定義:

작성 글자: 0원문 글자: 9,746작성 단락: 0/284