CAP đúng nghĩa — P bắt buộc, chọn C hay A
CAP không phải 'chọn 2 trong 3'. Khi partition (P) xảy ra — và nó SẼ xảy ra — mới phải chọn giữa consistency và availability. Diễn giải đúng và các hiểu lầm.
TL;DR: Định lý CAP hay bị đọc sai thành "chọn 2 trong 3". Cách đọc đúng: Partition (P) không phải một lựa chọn — mạng SẼ phân mảnh, nên P luôn phải chịu. Khi partition đang xảy ra, mỗi node bị cắt phải chọn giữa C (linearizability — từ chối phục vụ để không trả lời sai) và A (vẫn trả lời nhưng có thể trả dữ liệu cũ). Ngoài lúc partition, một hệ có thể có cả C lẫn A. Phân loại thực dụng: CP (hy sinh khả dụng khi partition) vs AP (hy sinh nhất quán khi partition). "C" trong CAP nghĩa hẹp là linearizability — mức yếu hơn (causal) vẫn khả dụng khi partition, nằm ngoài đánh đổi gắt này.
"Hệ của mình chọn CA — nhất quán và khả dụng, khỏi lo partition." Câu này nghe hợp lý nhưng sai về bản chất: trong một hệ phân tán thật, mạng sẽ rớt gói, đứt cáp, nghẽn — partition không phải rủi ro bạn chọn gánh hay không, nó là điều kiện tự nhiên. Không thể "bỏ P" như bỏ một topping. Hiểu lầm "2 trong 3" khiến vô số kiến trúc chọn sai và ngạc nhiên khi hệ vừa treo vừa sai lúc mạng có sự cố.
Bài này sửa lại cách đọc CAP cho đúng định lý gốc (Gilbert & Lynch, 2002), làm rõ vì sao P bắt buộc, vì sao đánh đổi thật chỉ hiện ra khi partition, và vì sao "C" ở đây hẹp hơn "nhất quán" nói chung.
1. Analogy — hai chi nhánh ngân hàng mất liên lạc
Một ngân hàng có hai chi nhánh dùng chung sổ số dư, nối nhau bằng một đường truyền. Bình thường mọi giao dịch đồng bộ tức thì. Rồi đường truyền đứt (partition): chi nhánh A không còn nói chuyện được với B. Một khách tới A rút tiền. A đứng trước đúng hai lựa chọn:
- Giữ nhất quán (C): "Xin lỗi, hiện chưa xác nhận được số dư mới nhất bên B, tôi không phục vụ để tránh cho rút quá tay." → đúng nhưng từ chối khách (mất availability).
- Giữ khả dụng (A): "Cứ cho rút theo số dư tôi đang có." → phục vụ được, nhưng nếu B cũng vừa cho rút thì tổng có thể âm — dữ liệu không nhất quán.
Không có cửa thứ ba khi đường đã đứt. Và không thể "chọn không đứt cáp" — đứt cáp là sự cố tự nhiên, không phải tuỳ chọn. Đó là toàn bộ tinh thần CAP.
| Ngân hàng hai chi nhánh | Hệ phân tán |
|---|---|
| Đường truyền đứt giữa A và B | Network partition (P) |
| "Không phục vụ để khỏi sai" | Chọn C — hy sinh availability |
| "Cứ phục vụ, có thể sai số dư" | Chọn A — hy sinh consistency |
| Không thể "chọn không đứt cáp" | P không phải lựa chọn — luôn phải chịu |
CAP không phải "chọn 2 trong 3". Đọc đúng: P luôn có thể xảy ra, nên câu hỏi thật là — khi partition, bạn chọn C hay A? Ngoài lúc partition thì có cả hai.
2. Định nghĩa chính xác — CAP của Gilbert & Lynch (2002)
Ba chữ trong phát biểu hình thức (Gilbert & Lynch, 2002 — chứng minh chặt phỏng đoán của Brewer):
- C — Consistency: ở đây nghĩa hẹp là linearizability (bài 01) — mọi thao tác thấy một sự thật mới nhất duy nhất. Không phải chữ C trong ACID.
- A — Availability: mọi request tới một node không lỗi đều nhận được một phản hồi (không treo vô hạn). Lưu ý: không đòi phản hồi nhanh, chỉ đòi có phản hồi.
- P — Partition tolerance: hệ vẫn tiếp tục hoạt động dù mạng làm rớt/hoãn tuỳ ý các thông điệp giữa các node.
Định lý phát biểu: không hệ nào đồng thời bảo đảm cả C và A khi có partition. Chứng minh trực giác: partition cắt hệ thành hai nửa không nói chuyện được. Nếu nửa này nhận một ghi, nửa kia không thể biết → để trả lời một đọc bên nửa kia mà vẫn linearizable (thấy ghi mới), nó buộc phải chờ liên lạc phục hồi (mất A) hoặc trả giá trị cũ (mất C). Không có lối thoát thứ ba.
CAP không nói "hệ phân tán luôn phải hy sinh C hoặc A". Khi mạng lành, một hệ có thể vừa linearizable vừa khả dụng. Đánh đổi C-vs-A chỉ bật lên trong lúc có partition. Vì thế cách phân loại đúng là "khi partition thì nghiêng về đâu", không phải "vĩnh viễn thiếu một chữ".
3. Vì sao P không phải lựa chọn — và "CA" là ảo tưởng?
Điểm sai cốt lõi của cách đọc "2 trong 3" là coi P ngang hàng C và A như thể chọn được. Nhưng C và A là thuộc tính bạn thiết kế, còn P là điều kiện môi trường áp lên bạn. Bạn không "bật/tắt" partition — cáp đứt, switch chết, GC pause dài, mạng nghẽn đều tạo partition, và trong một hệ nhiều máy chúng sẽ xảy ra.
Do đó "CA" (nhất quán + khả dụng, bỏ P) chỉ tồn tại trên một máy đơn không có mạng để mà phân mảnh. Ngay khi có từ hai node nối bằng mạng, bạn đã phải chịu P — nên lựa chọn thật luôn là CP hay AP:
| Nhóm | Khi partition | Đánh đổi | Ví dụ (bài 05) |
|---|---|---|---|
| CP | Từ chối phục vụ ở phía thiểu số để giữ linearizability | Mất availability | etcd, ZooKeeper, HBase |
| AP | Vẫn phục vụ mọi phía, hội tụ sau | Mất linearizability (chấp nhận cũ/xung đột) | Cassandra, Dynamo-style |
flowchart TD
P{"Mang co partition khong?"}
P -->|"Khong (mang lanh)"| BOTH["Co the vua C vua A"]
P -->|"Co"| CHOICE{"Node bi cat chon gi?"}
CHOICE -->|"Giu linearizable"| CP["CP: tu choi phuc vu (mat A)"]
CHOICE -->|"Van phuc vu"| AP["AP: tra loi co the cu (mat C)"]Quorum (w + r > n) là nơi CAP hiện ra rất cụ thể. Một hệ đòi strict majority quorum để đọc/ghi: khi partition cắt cụm thành 3 | 2 (với n = 5), phía thiểu số 2 node không gom đủ đa số → nó phải từ chối (CP — mất availability ở phía thiểu số). Muốn phía thiểu số vẫn phục vụ, phải nới quorum (sloppy quorum / hinted handoff) → ngả sang AP, chấp nhận mất linearizability. Chính con số quorum là cái "núm vặn" C-vs-A.
Cụm 5 node, n = 5, dùng strict majority quorum: ghi cần 3 node xác nhận, đọc cần 3 node. Một partition cắt cụm thành hai nhóm: nhóm X có 3 node, nhóm Y có 2 node. Trong lúc partition:
Nhóm nào vẫn nhận được ghi mới, nhóm nào buộc từ chối? Và hệ này đang hành xử theo CP hay AP? Lần theo định nghĩa quorum + định lý CAP ở mục 2-3 trước khi đọc tiếp.
Lần theo cơ chế: ghi cần 3 node xác nhận. Nhóm X (3 node) vừa đủ gom một majority quorum trong nội bộ → vẫn nhận ghi và đọc được, và vì mọi majority quorum đều giao nhau, nó giữ được một sự thật nhất quán. Nhóm Y (2 node) không thể gom nổi 3 → mọi ghi/đọc cần quorum đều bị từ chối (client bên Y bị treo/lỗi). Hệ hy sinh availability ở phía thiểu số để không bao giờ có hai phía cùng nhận ghi mâu thuẫn — đó chính là hành vi CP. Nếu thay vào đó ta cho nhóm Y cứ phục vụ bằng dữ liệu cục bộ (nới quorum), hệ thành AP: Y vẫn khả dụng nhưng có thể trả giá trị cũ và tạo xung đột phải hoà giải sau. Không có cấu hình nào cho cả "Y vẫn phục vụ" lẫn "linearizable" trong lúc partition — đúng như định lý.
4. PACELC — bức tranh đầy đủ hơn: còn cả latency
CAP chỉ nói về lúc partition, nhưng phần lớn thời gian mạng lành — và ngay cả lúc lành vẫn có một đánh đổi CAP bỏ sót: latency. PACELC (Abadi, 2012) mở rộng: nếu Partition thì chọn A hay C; Else (mạng lành) thì chọn Latency hay Consistency.
Ý là: kể cả không partition, muốn linearizability bạn vẫn phải phối hợp giữa các bản sao mỗi thao tác (bài 01, mục 5) → chậm hơn. Muốn nhanh (đọc từ bản gần nhất) thì phải nới nhất quán. Vì vậy một hệ AP như Cassandra thường là PA/EL (partition → A; else → thiên latency), còn etcd là PC/EC (luôn nghiêng consistency). PACELC nhắc: đánh đổi nhất quán không chỉ sống ở lúc sự cố hiếm, nó hiện diện mỗi thao tác dưới dạng latency.
5. Pitfall — "2 trong 3" và tưởng chọn được CA
❌ "He minh la CA: nhat quan + kha dung, khong lo partition"
-> Ao tuong: khong the bo P trong he nhieu node. Mang SE phan manh.
"CA" chi ton tai tren mot may don. Thuc te ban dang la CP hoac AP.
❌ "Chon 2 trong 3 mot lan cho mãi mãi"
-> SAI: danh doi C-vs-A chi kich hoat KHI partition. Ngoai luc do co
ca hai. Phan loai dung: "khi partition thi nghieng ve dau".
❌ "C trong CAP = nhat quan noi chung, nen causal cung dinh CAP"
-> C o day nghia HEP = linearizability. Causal consistency VAN kha dung
khi partition -> no khong bi ket trong danh doi gat cua CAP.
Hệ quả: đội tự nhận "CA" rồi không thiết kế cho lúc partition → khi cáp thật sự đứt, hệ vừa treo (vì kỳ vọng khả dụng) vừa trả dữ liệu sai (vì kỳ vọng nhất quán), không có hành vi được định nghĩa rõ.
Hướng đúng: bắt đầu từ "P luôn có", rồi hỏi đúng một câu cho từng loại dữ liệu: "khi partition, dữ liệu này nên từ chối (CP) hay phục vụ có thể cũ (AP)?". Số dư/khoá/uniqueness → CP. Giỏ hàng/feed/like → AP. Và nhớ PACELC: kể cả lúc mạng lành, linearizability vẫn tính phí bằng latency.
6. 📚 Đào sâu — nguồn gốc & tài liệu
- Designing Data-Intensive Applications (Kleppmann) — Chương 9, mục "The Cost of Linearizability" & "CAP" — Kleppmann phê phán cách phát biểu CAP phổ biến và nhấn: đánh đổi chỉ về linearizability vs availability khi partition.
- Gilbert & Lynch (2002), "Brewer's Conjecture and the Feasibility of Consistent, Available, Partition-Tolerant Web Services" (ACM SIGACT News) — paper chứng minh hình thức định lý CAP; nguồn định nghĩa C/A/P ở mục 2.
- Eric Brewer (2012), "CAP Twelve Years Later: How the 'Rules' Have Changed" (IEEE Computer) — chính tác giả phỏng đoán CAP nói rõ "2 trong 3" là gây hiểu lầm, và đánh đổi chỉ áp khi partition.
- Daniel Abadi (2012), "Consistency Tradeoffs in Modern Distributed Database System Design" — giới thiệu PACELC (mục 4).
Ghi chú: Brewer nhấn rằng partition hiếm, nên hệ nên tối ưu cho lúc mạng lành và chỉ phát hiện + xử lý partition khi nó xảy ra — không nên "hy sinh vĩnh viễn" một chữ như cách đọc "2 trong 3" gợi ý.
7. Liên hệ các bài khác
- Bài 01 — Linearizability: "C" trong CAP chính là linearizability; "kém khả dụng khi partition" ở bài 01 được CAP hình thức hoá tại đây.
- Bài 02 — Causal consistency: mức nhất quán mạnh nhất vẫn khả dụng khi partition — nằm ngoài đánh đổi gắt của CAP, giải thích vì sao nó là "điểm ngọt".
- Bài 05 — Case study: etcd/ZooKeeper (CP) vs Cassandra (AP) là hiện thân cụ thể của lựa chọn trong bài này.
- Module 2 — Leaderless replication: quorum
w + r > nvà sloppy quorum là núm vặn CP↔AP thực tế.
8. Tóm tắt
- P không phải lựa chọn: mạng phân tán sẽ phân mảnh, nên P luôn phải chịu. "CA" chỉ có trên máy đơn.
- Đánh đổi thật chỉ bật khi partition: node bị cắt chọn C (từ chối để đúng) hay A (phục vụ nhưng có thể sai). Ngoài lúc đó có cả hai.
- Phân loại thực dụng: CP (etcd/ZooKeeper — hy sinh availability) vs AP (Cassandra — hy sinh linearizability).
- "C" trong CAP nghĩa hẹp = linearizability, không phải C của ACID; causal vẫn khả dụng khi partition nên không kẹt trong đánh đổi này.
- PACELC: kể cả mạng lành (Else), vẫn phải chọn Latency vs Consistency — nhất quán tính phí mỗi thao tác, không chỉ lúc sự cố.
- Cách chọn: mặc định "P luôn có", rồi hỏi từng loại dữ liệu — từ chối (CP) hay phục vụ có thể cũ (AP).
9. Tự kiểm tra
Q1Vì sao nói "P không phải một lựa chọn" và "CA không tồn tại trong hệ phân tán thật"? P khác C và A ở điểm bản chất nào?▸
Vì C và A là thuộc tính bạn thiết kế, còn P là điều kiện môi trường áp lên bạn. Bạn không bật/tắt được partition: cáp đứt, switch chết, GC pause dài, mạng nghẽn đều tạo partition, và trong hệ nhiều máy chúng sẽ xảy ra. Không thể "chọn không đứt cáp".
Do đó "CA" (nhất quán + khả dụng, bỏ P) chỉ tồn tại trên máy đơn không có mạng để phân mảnh. Ngay khi có từ hai node nối bằng mạng, bạn đã phải chịu P — nên lựa chọn thật luôn là CP hay AP, không phải CA.
Q2CAP nói đánh đổi C-vs-A "chỉ kích hoạt khi partition". Điều đó nghĩa là gì với một hệ khi mạng đang lành? Vì sao cách đọc "chọn 2 trong 3 một lần cho mãi mãi" là sai?▸
Khi mạng lành, mọi node nói chuyện được với nhau, nên một hệ hoàn toàn có thể vừa linearizable vừa khả dụng — không phải hy sinh chữ nào. Đánh đổi chỉ hiện ra trong lúc một partition đang cắt hệ.
"Chọn 2 trong 3 một lần cho mãi mãi" sai vì nó gợi ý hệ vĩnh viễn thiếu một thuộc tính. Thực tế hệ chỉ phải nghiêng về C hoặc A tại thời điểm partition; phân loại đúng là "khi partition thì nghiêng về đâu" (CP/AP), chứ không phải "luôn thiếu A" hay "luôn thiếu C".
Q3Cụm 5 node, strict majority quorum (ghi/đọc cần 3). Partition cắt thành nhóm 3 và nhóm 2. Mô tả hành vi mỗi nhóm và cho biết hệ đang là CP hay AP. Vì sao không thể vừa "nhóm 2 phục vụ" vừa linearizable?▸
Nhóm 3 gom đủ majority quorum trong nội bộ → vẫn nhận ghi và đọc, giữ một sự thật nhất quán (mọi majority quorum giao nhau). Nhóm 2 không đạt nổi 3 → từ chối mọi thao tác cần quorum (client bên đó treo/lỗi). Hệ đang là CP: hy sinh availability ở phía thiểu số.
Không thể vừa cho nhóm 2 phục vụ vừa linearizable: nếu nhóm 2 nhận ghi bằng dữ liệu cục bộ trong khi nhóm 3 cũng nhận ghi, hai phía tạo ra hai sự thật mâu thuẫn không ai biết của ai — vi phạm "một sự thật mới nhất duy nhất". Cho nhóm 2 phục vụ = nới quorum = ngả sang AP, chấp nhận dữ liệu cũ/xung đột. Đúng như định lý: khi partition, không có cả C lẫn A.
Q4Vì sao "C" trong CAP không bao trùm causal consistency? Điều này nói gì về chỗ đứng của causal so với đánh đổi CAP?▸
Vì "C" trong CAP có nghĩa hẹp là linearizability — một sự thật mới nhất duy nhất khớp real-time. Định lý chứng minh linearizability và availability không thể cùng có khi partition. Nhưng causal consistency yếu hơn: nó không đòi total order/recency, nên một node bị cắt vẫn phục vụ đọc/ghi từ dữ liệu mình có mà vẫn giữ đúng thứ tự nhân quả.
Nghĩa là causal không kẹt trong đánh đổi gắt của CAP: nó là mức nhất quán mạnh nhất mà một hệ vẫn có thể luôn khả dụng dưới partition. CAP chỉ chặn cái trần linearizable; dưới trần đó, causal cho ta "vừa nhất quán hợp lý vừa khả dụng".
Q5PACELC thêm gì so với CAP? Vì sao một hệ chọn linearizability phải "trả phí" ngay cả khi mạng không hề partition?▸
PACELC thêm nửa "Else Latency-vs-Consistency": nếu Partition thì chọn A hay C; else (mạng lành) thì chọn Latency hay Consistency. CAP chỉ nói lúc partition; PACELC chỉ ra đánh đổi còn tồn tại lúc mạng lành, dưới dạng latency.
Một hệ linearizable phải "trả phí" kể cả khi không partition vì để đảm bảo recency, mỗi thao tác phải phối hợp giữa các bản sao (hỏi leader/quorum đồng bộ) thay vì trả lời ngay từ bản gần nhất — thêm ít nhất một vòng đi-về mạng, tức chậm hơn. Muốn nhanh thì phải nới nhất quán. Vì thế nhất quán không chỉ tốn khả dụng lúc sự cố hiếm, mà tính phí latency ở mỗi thao tác.
Bài tiếp theo: Consensus qua Raft — leader election, log replication, commit
Bài này có giúp bạn hiểu bản chất không?
Hỏi đáp về bài này
Chưa có câu hỏi
Có gì chưa rõ trong bài? Đặt câu hỏi đầu tiên — câu trả lời từ cộng đồng giúp bạn (và người sau).
Đặt câu hỏi đầu tiên