前章まで、私たちは通信を信頼でき、秘匿されたものにした。だが現代のサービスは、1台のサーバーでは動かない。障害に備え、負荷を分散するため、データは複数のマシンに複製(レプリケーション)される。
ここで、避けようのない哲学的難問が生まれる。複数のコピーが存在するとき、それらは本当に「同じ」だと言い切れるのか? そして、マシン間の通信が切れたとき——「正しさ」と「動き続けること」の、どちらを取るのか。 この選べない選択を定式化したのがCAP定理だ。この章では、分散システムの宿命と、それに立ち向かう思想を解く。
3つのうち、2つしか選べない — 歴史の必然
分散システムには、3つの望ましい性質がある。CAP定理(2000年、Eric Brewerが提唱、後に証明)は、この3つを同時にすべて満たすことはできないと述べる。
| 性質 | 意味 |
|---|---|
| C: 一貫性(Consistency) | どのノードに聞いても、常に最新の同じ答えが返る |
| A: 可用性(Availability) | 生きているノードは、必ず応答を返す(待たせない) |
| P: 分断耐性(Partition tolerance) | ネットワークが分断されても、システムは動き続ける |
ここで決定的な事実がある。分散システムにおいて、ネットワーク分断(P)は「選択肢」ではなく「避けられない現実」だ。ケーブルは切れ、ルーターは落ち、パケットは失われる(第13章)。だから実質的な選択は、分断が起きたとき、C と A のどちらを諦めるかに絞られる。
厨房のアナロジー — 2店舗の予約台帳が食い違う
あなたのレストランが、本店と支店の2店舗を持つとする。予約台帳(データ)は両店で共有し、常に同じ内容に保ちたい(一貫性)。普段は電話で「今この予約が入った」と即座に同期している。
ある日、2店舗をつなぐ電話が不通になった(ネットワーク分断)。ここに客が来て「今夜のディナーを予約したい」と言う。あなたは二択を迫られる。
- 一貫性を優先(CP): 「本店に確認が取れないので、今は予約をお受けできません」と断る。台帳の食い違いは絶対に防げるが、客を待たせる/断る(可用性を犠牲)。
- 可用性を優先(AP): 「承りました」と支店の台帳だけに書く。客は満足するが、もし本店も同じ席を売っていたらダブルブッキングになる(一貫性を犠牲)。電話が復旧した後で、食い違いを調整するしかない。
私が飲食店で店舗間の予約管理に関わったとき、まさにこのジレンマがあった。「確実さを取って断るか、機会損失を避けて受けるか」。どちらが正解かは、ビジネスの性質による。CAP定理は、この判断が技術ではなく事業上の選択だと教えてくれる。
通常時: C も A も両立できる
┌─────┐ 同期 ┌─────┐
│本店 │◀──────▶│支店 │ 台帳は常に一致
└─────┘ └─────┘
分断時(電話不通): C か A の二択を迫られる
┌─────┐ ╳ ┌─────┐
│本店 │──断線──│支店 │
└─────┘ └─────┘
CP: 「確認できないので断る」(正しいが止まる)
AP: 「とりあえず受ける」(動くが食い違いうる)
CP型とAP型 — 実システムの選択
現実のデータストアは、この思想の違いで design が分かれている。
CP型(一貫性優先): 分断時、正しさを保証できない側は応答を止める。「間違った答えを返すくらいなら、答えない」。
- 例: 銀行の残高、在庫管理、分散合意を使うシステム(etcd, ZooKeeper, 多くのRDBMS構成)
- 残高が食い違うのは許されない。多少待たせても正しさを守る
AP型(可用性優先): 分断時も応答を返し続ける。食い違いは後で解決する(結果整合性, Eventual Consistency)。
- 例: SNSのタイムライン、ショッピングカート、DNS(Cassandra, DynamoDBの一部設定)
- 「いいね」の数が一瞬ずれても構わない。止まる方が損失
CP型: 「正しくないなら答えない」
分断発生 → 少数派ノードは応答停止 → 復旧後に整合
(銀行残高: 食い違いは絶対NG)
AP型: 「とりあえず答える、後で直す」
分断発生 → 各ノードが応答継続 → 復旧後に矛盾を解決(結果整合性)
(SNSいいね数: 一時的なズレは許容)
第11章のWAL(ログを先に書く)を思い出してほしい。分散システムのレプリケーションは、突き詰めれば「このログを他ノードにどう伝え、どう合意するか」という問題だ。「過半数のノードが合意したら確定」とする分散合意アルゴリズム(Paxos, Raft)は、CP型システムの心臓部になっている。
なぜ「結果整合性」で許されるのか
AP型の「結果整合性」は手抜きではなく、意図的な設計だ。多くのユースケースでは、厳密な一貫性より、止まらないことの方が価値が高い。ショッピングサイトが「一貫性確認中」で真っ白になるより、多少古い在庫数でも表示され続ける方が、売上を守れる。CAP定理の教えは、「万能はない。要件に応じてトレードオフを選べ」ということだ。
PACELC — CAPの先へ(補足)
CAP定理には続きがある。PACELCという拡張だ。CAPは「分断(P)が起きたとき A か C か」だけを語るが、現実には分断していない平常時(E: Else)にもトレードオフがある——低遅延(L: Latency)か 一貫性(C)かだ。
分断時(P)は A か C か。平常時(E)は L(レイテンシ)か C(一貫性)か。
全ノードに書き込みが行き渡るのを待てば一貫性は上がるが、応答は遅くなる。待たずに返せば速いが、一瞬古いデータを返しうる。「強い一貫性はレイテンシと引き換え」という、現場のエンジニアが日々直面する感覚を、PACELCは的確に言い当てている。
カオスエンジニアリング — あえて壊して強くする
CAP定理は「障害は必ず起きる」と教える。ならば、障害が起きてから慌てるのではなく、平常時にわざと障害を起こして、耐性を確かめておけばいい。この逆転の発想がカオスエンジニアリングだ。
Netflixが2011年に生んだ Chaos Monkey が象徴的だ。これは本番環境で、ランダムにサーバーを停止させるツールだ。正気の沙汰に聞こえるが、思想は深い。
「いつか必ず落ちるなら、都合のいい昼間に、監視された状態で、わざと落とす。」
深夜に予期せず落ちて大障害になるより、エンジニアが見ている日中に意図的に落とし、「本当に自動復旧するか」「冗長化は機能するか」を検証する。障害を制御された実験に変えるのだ。
カオスエンジニアリングは、科学の実験と同じ手順を踏む。
① 定常状態の定義: 「正常」を計測可能な指標で定義
(例: 注文成功率 99.9%、レイテンシ p99 < 300ms)
│
▼
② 仮説: 「サーバーを1台落としても、指標は維持されるはず」
│
▼
③ 実験: 本番(or 近い環境)で、あえて障害を注入
(インスタンス停止 / 遅延注入 / パケットロス / 依存サービス切断)
│
▼
④ 検証: 指標は維持されたか? → 崩れたら、それが「隠れた弱点」
│
▼
⑤ 改善して再実験。爆発半径(影響範囲)は小さく始め、徐々に広げる
ここでエラーバジェットという考え方が効く。「100%の稼働率」は幻想だ(CAP定理が示す通り)。そこでSREは、たとえば「99.9%の可用性」を目標に定める。すると許容される停止時間(エラーバジェット)が計算できる:
この43分を「使ってよい予算」とみなし、その範囲であえてリスクを取って実験や機能追加を行う。可用性を神格化せず、工学的に管理する——これがSRE(Site Reliability Engineering)の中核思想だ。
まとめ — 「おまじない」の消し方
- CAPは選べない選択:C・A・P の3つは同時に満たせない。分断(P)は不可避なので、実質は C か A の二択
- CP型 vs AP型:正しさを守って止まるか(銀行)、動き続けて後で整合するか(SNS)。事業要件が決める
- 結果整合性は設計:「止まらないこと」に価値がある領域では、一時的なズレを意図的に許容する
- カオスエンジニアリング:障害は必ず起きる。ならば平常時にあえて壊し、制御された実験で弱点を暴く
- エラーバジェット:100%は幻想。許容停止時間を予算とみなし、可用性を工学的に管理する
「なぜ完璧に一貫したシステムは作れないのか」「なぜNetflixはわざとサーバーを落とすのか」——その答えは、分散システムの宿命と、それを受け入れる思想にあった。これでPart 5——マシンとマシンの間の世界——を一巡した。
ここまでの旅は「どう動かすか」の話だった。次のPart 6からは、視点を「大量の計算をどう速くするか」、そしてAIを支える数学へ移す。まずは、数千のコアで押し切る怪物——GPUの超並列——の物理へ。
「完璧なシステムは存在しない。優れたエンジニアと平凡なエンジニアを分けるのは、障害を無くそうとするか、障害と共に生きる術を設計するかの違いだ。壊れることを前提に作られたものだけが、本当に壊れにくい。」