Tech Learning Daily

2026-10-03 (Sat) — 第 78 号
AI が毎朝届ける、ソフトウェア技術の基礎解説
Arch 🌳 応用 ⏱ 約 8 分

etcd は過半数を割ると書き込みが止まるのに、なぜ DynamoDB は止まらないのか?

少し前に、etcd は過半数のノードを失うと書き込みも読み取りも止まる、という話を読んだはずだ。しかし同じ「複数台のサーバーでデータを持つ分散システム」である DynamoDB や Cassandra は、ノード間の通信が切れても書き込みを受け付け続ける。この違いは実装の巧拙ではなく、CAP定理という同じ制約に対する2つの異なる答え方の結果だ。

🎯 3 行まとめ
  • CAP定理の本当の制約は「3つから2つを選ぶ」ではなく、ネットワーク分断が起きた瞬間にだけ強制される「一貫性か可用性かの二択」である。
  • etcd は分断時に少数派側の書き込みを止めて一貫性を守り(CP)、DynamoDB/Cassandra は分断時も各レプリカが書き込みを受け付け可用性を守る(AP)。
  • 分断していない平常時にも「一貫性を強く求めるほど遅延が増える」という別のトレードオフが常にあり、これを拡張したのが PACELC だ。

CAP定理が指す3つの性質

CAP定理は、Eric Brewer が2000年に提唱した予想で、2002年に Seth Gilbert と Nancy Lynch が形式的に証明した。複数ノードにデータを分散させるシステムは、次の3つの性質を同時には満たせないという主張だ。

Consistency(一貫性)は、どのノードに読み取りを投げても直前までの書き込みを反映した最新の値が返ること。Availability(可用性)は、生きているノードへのリクエストが必ず何らかの応答を返すこと(エラーで拒否するのではなく)。Partition tolerance(分断耐性)は、ノード間のネットワークが一部切れて通信できなくなっても、システム全体が動作し続けることだ。

Gilbert と Lynch の証明は背理法による。ネットワークが分断され、通信できない2つのノード A・B それぞれに書き込み要求が届いたとする。A と B は互いの状況を知る手段がないため、両方が応答すれば一方は古い値を返す可能性を排除できず一貫性が崩れ、一貫性を守ろうとすればどちらか一方は応答を拒否するしかなく可用性が崩れる。

Partition tolerance は「選べる選択肢」ではない

CAP定理は「3つの性質から2つだけ選べる」としてよく紹介される。しかし実際には、複数のノードをネットワークでつないで分散システムを組む以上、パケットロスや機器障害によるネットワーク分断は現実に起こり続ける前提条件であり、分断耐性を「捨てる」という選択は実質的に「分散システムをやめる」ことに等しい。つまり P は選択肢ではなく、残る選択は分断が実際に起きた瞬間にCとAのどちらを諭すかだけに絞られる。

       ネットワーク分断が発生
               │
     ┌─────────┴─────────┐
     ▼                   ▼
┌──────────┐        ┌──────────┐
│ Node 1, 2│        │  Node 3  │
│ (多数派) │        │ (少数派) │
└──────────┘        └──────────┘
     │                   │
     ▼                   ▼
 write: 成立         write: 待機→失敗
        

この「分断時だけ発生する二択」を、合意を取るまで書き込みを確定させない設計(CP)にするか、各ノードが単独でも書き込みを受け入れて後で収束させる設計(AP)にするかが、データストアごとの個性になる。

🍱 たとえるなら

本社と支店をつなぐ電話線が切れた場面を想像してほしい。支店の窓口には客が並んでいるが、本社の在庫台帳を今確認する手段はない。支店長には二択しかない。「本社と連絡が取れるまで販売を止める」(一貫性を優先して可用性を諭す=CP)か、「とりあえず売ってしまい、電話が復旧してから本社と帳簿を合わせる」(可用性を優先して一貫性を諭す=AP)かだ。電話線がつながっている間はこの二択自体が発生しない——CAP定理が「分断時にだけ」顔を出すのも同じ理由である。

実例: etcd は CP、DynamoDB/Cassandra は AP

etcd は Raft という合意アルゴリズムで動く。書き込みはクォーラム(ノード数の過半数)にログが複製されるまで確定しない。ネットワークが分断されると、過半数側のノード群は合意を成立させ続けられるので書き込みに応答できるが、少数派側のノードは過半数に届かないため書き込みを確定できず、応答を保留する——つまり一貫性を守るために可用性を諭す CP の設計だ。

対して DynamoDB は、読み取り操作の既定が結果整合性(eventual consistency)の読み取りで、直前の書き込みをまだ反映していない可能性がある代わりに常に応答する。最新の値を保証する強整合性(strong consistency)の読み取りが必要な場合はConsistentReadパラメータをtrueに指定するが、消費する読み取りキャパシティは結果整合性読み取りの2倍になる。Cassandra も同系列の設計で、クエリごとにONE(1レプリカの応答で確定、最速だが分断中の少数派でも応答できる)・QUORUM(過半数の応答が必要)・ALL(全レプリカの応答が必要、最も強い一貫性だが1台でも分断されると失敗する)という consistency level を選べる。

cqlsh> CONSISTENCY QUORUM;
cqlsh> SELECT * FROM users WHERE id = 1;

この consistency level を ONE にすると分断中でも応答が返りやすくなる代わりに古い値を読む可能性が増え、ALLにすると読む値は常に最新になる代わりに1レプリカでも分断・停止していると即座に失敗する。どちらが正しいかはアプリケーションが「古い値を読むリスク」と「応答が来ないリスク」のどちらを嫌うかで決まる。

PACELC: 分断していない時にも効くトレードオフ

CAP定理が言及するのは「分断が起きている間」だけだ。Daniel Abadi は2010年のブログ記事でこれを拡張し、後に論文としてまとめたPACELCを提案した。読み方は「分断(Partition)が起きたら Availability か Consistency を選び、そうでなければ(Else)Latency か Consistency を選ぶ」というものだ。

DynamoDB は PACELC の分類でPA/EL(分断時は可用性優先、平常時は低レイテンシ優先)、HBase のように強い一貫性を常に守る設計はPC/EC(分断時も平常時も一貫性優先)に分類される。つまり AP 型のデータストアであっても、分断がなければ常に低レイテンシというわけではなく、強整合性読み取りを要求するほど複数レプリカへの確認が必要になり応答は遅くなる——分断の有無に関係なく、一貫性の強さと速さは常に綱引きをしている。

💼 実務でどう出会うか

マイクロサービスの設定ストアに etcd や ZooKeeper を選ぶ場面、DynamoDB の GetItem で ConsistentRead: true を指定するか迷う場面、Cassandra のクエリに CONSISTENCY を指定する場面で、CAP/PACELC の考え方がそのまま判断基準になる。在庫数や決済状態のように古い値を読むと事故になるデータは一貫性側に、SNS のいいね数のように多少古くても困らないデータは可用性・速度側に倒す、という形で設計の分岐点になる。

⌨️ 手を動かす(5 分)

3 台構成の etcd クラスタをローカルに作り、1 台をネットワークから切り離して「少数派になったノードは書き込みを確定できない」ことを確認する。

docker network create cap-demo
for i in 1 2 3; do
  docker run -d --name etcd$i --network cap-demo \
    quay.io/coreos/etcd:v3.5.30 etcd \
    --name etcd$i \
    --initial-advertise-peer-urls http://etcd$i:2380 \
    --listen-peer-urls http://0.0.0.0:2380 \
    --listen-client-urls http://0.0.0.0:2379 \
    --advertise-client-urls http://etcd$i:2379 \
    --initial-cluster-token cap-demo \
    --initial-cluster etcd1=http://etcd1:2380,etcd2=http://etcd2:2380,etcd3=http://etcd3:2380 \
    --initial-cluster-state new
done
sleep 5

# etcd3 をネットワークから切断し、少数派(1/3)にする
docker network disconnect cap-demo etcd3

# 多数派側(etcd1)への書き込みは成功する
docker exec etcd1 etcdctl put foo bar

# 少数派側(etcd3)への書き込みは既定5秒でタイムアウトする
docker exec etcd3 etcdctl put foo bar

# 後片付け
docker network connect cap-demo etcd3
docker rm -f etcd1 etcd2 etcd3 && docker network rm cap-demo

etcd1への put は即座に成功する一方、分断されたetcd3への put は過半数に届かないままリーダーとの合意が取れず、etcdctlの既定のcommand-timeoutである5秒後にcontext deadline exceededで失敗するはずだ。これが CP システムが可用性を諭している瞬間そのものである。

🙅 よくある誤解
  • CAP定理は平常時でも3つの性質から2つしか得られないという理論だ — 実際にはネットワーク分断が起きている間だけ強制される制約で、分断がなければ一貫性も可用性も両立できる。
  • NoSQL は全部 AP、RDB は全部 CP だ — CP/AP はデータストアの合意方式で決まる。Dynamo系のCassandraやDynamoDBはAP寄りだが、Raft/Paxosで合意を取るetcdやZooKeeperはNoSQLでもCPであり、カテゴリではなく実装で決まる。
  • 可用性を優先(AP)すれば常に速く応答できる — AP でも強整合性読み取りを要求すれば複数レプリカへの確認が必要になり遅延は増える。速さと一貫性の綱引きは PACELC が示すとおり分断の有無とは別に常に存在する。
📖 用語ミニ辞典
CAP定理
分散システムは一貫性・可用性・分断耐性の3つを同時には満たせないという定理。
クォーラム(quorum)
合意や書き込み確定に必要なノード数の過半数。
結果整合性(eventual consistency)
書き込み直後は古い値が返る可能性があるが、時間が経てば最終的に全ノードが一致する整合性モデル。
強整合性(strong consistency)
読み取りが常に直前までの最新の書き込みを反映することを保証する整合性モデル。
PACELC
CAPを拡張し、分断時はA/C、平常時はLatency/Consistencyの選択が常に存在するとした考え方。
🔗 もっと深く