Thuật toán Ứng dụng — DP, String, Big Data & hơn nữa
37/38
Bài 37 / 38~26 phútThuật toán phân tánMiễn phí lượt xem

Case study — Cassandra ring & etcd Raft

Cassandra ghép consistent hashing + tunable quorum + gossip + Merkle tree ra sao; etcd dùng Raft cho config nhất quán mạnh thế nào. Vì sao một chọn AP, một chọn CP.

TL;DR: Cassandra và etcd đều là hệ phân tán nổi tiếng, nhưng chúng đặt cược khác nhau trên tam giác CAP. Cassandra ghép bốn thuật toán từ module này — consistent hashing phân mảnh dữ liệu lên ring, tunable quorum cho phép điều chỉnh mức nhất quán theo request, gossip lan truyền trạng thái membership, Merkle tree sửa chênh lệch giữa các replica — để đạt AP: luôn ghi được, chấp nhận eventual consistency. etcd chọn CP: Raft đảm bảo mọi đọc/ghi đều nhất quán mạnh, đổi lấy việc mất availability khi mất quorum. Bài học từ hai hệ thống: không có thiết kế "tốt hơn" — chỉ có thiết kế phù hợp với bài toán.

Cassandra
Apache Cassandra

AP — consistent hashing + tunable quorum + gossip + Merkle tree

etcd
etcd

CP — Raft consensus cho config/service-discovery nhất quán mạnh

Phần A — Cassandra: bốn thuật toán ghép lại

1. Bài toán: dữ liệu người dùng ở quy mô Netflix

Apache Cassandra được thiết kế để lưu hàng trăm terabyte dữ liệu trải trên hàng trăm đến hàng nghìn node, không có single point of failure. Netflix dùng Cassandra để lưu viewing history, bookmark và user preferences — hệ thống phải luôn ghi được ngay cả khi một datacenter mất kết nối.

Yêu cầu:

  1. Phân mảnh — 100 TB dữ liệu phải phân bố đều trên hàng trăm node.
  2. Nhất quán — replica phải đồng bộ, phát hiện và sửa chênh lệch.
  3. Membership — mỗi node cần biết toàn bộ cluster đang có node nào, node nào đang chết.
  4. Fault tolerance — vẫn hoạt động khi một số node fail.

Cassandra giải quyết bốn yêu cầu này bằng bốn thuật toán từ module này:

2. Consistent hashing: token ring và virtual node

Cassandra dùng token ring — mỗi key được hash thành token trong không gian [0, 2^64), mỗi node sở hữu một khoảng token. Mỗi node vật lý được nhân bản thành nhiều virtual node (vnode) — mặc định 16 vnode mỗi node từ Cassandra 4.0 (bản cũ dùng 256) — để đảm bảo phân bố tải đều ngay cả khi node có phần cứng khác nhau.

Plain Text
-- Cassandra token assignment (đơn giản hóa)
-- Node A: owns tokens [100, 400), [700, 900)   (2 vnode)
-- Node B: owns tokens [0, 100), [400, 700)      (2 vnode)
-- Node C: owns tokens [900, 2^64) wrap [0, 0)   (1 vnode)

function getCoordinator(partitionKey):
    token <- murmur3Hash(partitionKey)    -- Cassandra dùng Murmur3
    return lookupRing(ring, token)        -- binary search, O(log(N×vnode))

Khi thêm node mới vào Cassandra cluster, chỉ vnode của node mới cần streaming data từ node kế tiếp — phần còn lại không bị ảnh hưởng. Đây chính là tính chất K/N key di chuyển của consistent hashing từ bài 01 và mini-challenge vừa xong.

Replication factor (RF): Cassandra không chỉ lưu 1 bản — mặc định RF=3, nghĩa là mỗi key được lưu trên 3 node vật lý kế tiếp nhau trên ring. Khi node chết, 2 bản replica còn lại vẫn phục vụ được.

3. Tunable quorum: CAP tradeoff theo request

Cassandra cho phép client chỉ định consistency level cho từng request — đây là ứng dụng trực tiếp của quorum R+W>N từ bài 02:

Consistency levelÝ nghĩaR+W>N?Tradeoff
ONE1 replica xác nhận là đủKhông đảm bảoNhanh nhất, có thể đọc stale
QUORUMĐa số replica xác nhận (N/2 + 1)Có (với RF=3: R=W=2, R+W=4>3)Cân bằng latency và consistency
ALLTất cả replica xác nhậnNhất quán tuyệt đối, nhưng khi 1 node fail thì ghi/đọc fail
LOCAL_QUORUMĐa số replica trong 1 datacenterCó trong DCDùng cho multi-datacenter

Netflix thường dùng LOCAL_QUORUM cho đọc và LOCAL_QUORUM cho ghi — đảm bảo nhất quán trong mỗi datacenter nhưng không block khi một datacenter bị cô lập.

Plain Text
-- Write path với QUORUM (RF=3, cần 2 replica xác nhận)
coordinator <- getCoordinator(partitionKey)
replicas    <- getReplicaNodes(ring, partitionKey, replicationFactor=3)

-- Ghi song song đến cả 3 replica
for each replica trong replicas:
    sendWriteAsync(replica, mutation)

-- Chờ đủ quorum (2/3)
waitForAcks(count=2, timeout=10ms)

-- Nếu đủ 2 ack trong timeout: success
-- Nếu không: timeout error (replica thứ 3 vẫn được ghi async sau đó)

Đây chính là cơ chế AP của Cassandra: ghi thành công khi đủ quorum, ngay cả khi một số replica lag sau. Replica lag sẽ được sửa bởi anti-entropy.

Thử ngẫmNetflix dùng LOCAL_QUORUM cho cả đọc và ghi trong mỗi datacenter. Khi một datacenter bị cô lập hoàn toàn khỏi các DC khác, request trong DC đó còn thành công được không?

4. Gossip: membership và failure detection

Mỗi node Cassandra cần biết toàn bộ cluster — node nào đang sống, node nào đang chết, load của từng node là bao nhiêu. Với cluster 1 000 node, broadcast toàn bộ mỗi giây là không khả thi.

Cassandra dùng Gossip protocol (bài 05) — mỗi giây, mỗi node chọn ngẫu nhiên 1-3 node và trao đổi thông tin về trạng thái của mọi node mà cả hai biết. Sau O(log N) vòng, thông tin lan khắp cluster:

Plain Text
-- Cassandra gossip cycle (mỗi giây trên mỗi node)
peers <- selectRandom(cluster, count=3)   -- chọn 3 node ngẫu nhiên
for each peer trong peers:
    myState    <- localNodeState()        -- version, load, tokens, status
    peerGossip <- exchange(peer, myState)
    mergeState(peerGossip)                -- cập nhật bảng trạng thái local

-- Sau ~log2(1000) ≈ 10 vòng gossip: mọi node biết trạng thái mọi node khác

Gossip cũng chạy Phi Accrual Failure Detector — thay vì "timeout = dead", tính xác suất node thực sự chết dựa trên lịch sử heartbeat. Điều này tránh false positive khi network tạm thời chậm.

5. Merkle tree: anti-entropy repair

Dù có quorum, replica vẫn có thể chênh lệch theo thời gian (network partition tạm thời, node crash giữa write). Cassandra chạy anti-entropy repair định kỳ bằng Merkle tree (bài 03):

Bốn bước trao đổi Merkle qua mạng: ba bước đầu chỉ truyền hash, bước cuối mới truyền dữ liệu chênh lệch

Thứ đắt trong anti-entropy là dữ liệu truyền, không phải phép so. Ba bước đầu chỉ gửi hash — vài chục byte mỗi bước, bất kể token range có một triệu row hay mười triệu.

Thay vì so sánh từng row (hàng triệu row), Merkle tree cho phép xác định đúng đoạn token range bị chênh lệch trong O(log N) so sánh hash, rồi chỉ sync dữ liệu ở đoạn đó.

Phần B — etcd: Raft cho config nhất quán mạnh

6. Bài toán: source of truth của Kubernetes

etcd là key-value store phân tán được dùng làm backing store cho Kubernetes — lưu toàn bộ trạng thái cluster: pod, deployment, service, configmap, secret. Mỗi thay đổi trong Kubernetes (thêm pod, scale deployment, cập nhật config) phải được ghi vào etcd và được mọi control plane node nhìn thấy nhất quán.

Yêu cầu khác hoàn toàn với Cassandra:

  • Strong consistency bắt buộc: hai controller đọc config cùng lúc phải thấy cùng một giá trị — không thể có split-brain.
  • No stale read: nếu một node write replicas=5, node khác đọc ngay sau phải thấy 5, không phải 3.
  • Chấp nhận giảm availability: nếu mất quorum (mất hơn N/2 node), etcd từ chối phục vụ thay vì trả về data có thể stale.

7. Raft trong etcd: leader là single source of truth

etcd triển khai Raft (bài 06) — mọi write phải đi qua leader, được nhân bản vào log của đa số node trước khi commit:

Plain Text
-- etcd write path (Raft)
client gửi PUT /key value đến bất kỳ etcd node
    if node không phải leader: redirect về leader

-- Leader xử lý:
leader.log.append(entry={term, index, key, value})
-- Gửi AppendEntries đến tất cả follower song song
for each follower trong cluster:
    sendAppendEntries(follower, entry)

-- Chờ đa số xác nhận (3/5 node = quorum)
waitForAcks(count=majority)

-- Khi đủ quorum: commit entry, apply vào state machine
leader.commitIndex <- entry.index
leader.applyToStateMachine(entry)
-- Báo success cho client

Mọi read cũng đi qua leader (trong chế độ linearizable mặc định) để đảm bảo không có stale read. Đây là điểm khác với Cassandra: Cassandra có thể đọc từ bất kỳ replica nào với ONE, etcd luôn đọc từ leader.

Hai cột kiến trúc: Cassandra không leader với gossip và Merkle, etcd một leader với Raft

Mọi khác biệt ở hai cột đều truy được về một quyết định duy nhất. Chọn A thì không được có điểm nghẽn nào, nên không có leader, nên phải có gossip và Merkle để tự chữa về sau. Chọn C thì phải có một chỗ quyết định thứ tự, nên có leader, nên mất leader là ngừng ghi.

8. Vì sao etcd chọn CP, không AP như Cassandra

Đây là ứng dụng trực tiếp của CAP theorem:

Cassandraetcd
Use caseUser data, session, activity log — stale vài giây chấp nhận đượcKubernetes config, service discovery — split-brain có thể gây deploy loop hoặc double scheduling
CAP chọnAP (availability + partition tolerance)CP (consistency + partition tolerance)
Khi mất quorumVẫn đọc/ghi được với ONE, data có thể staleTừ chối phục vụ cho đến khi phục hồi quorum
Độ trễThấp (gossip, async)Cao hơn một chút (Raft roundtrip đến đa số)
Consistency modelTunable: eventual đến strongLinearizable (strong) by default

Kubernetes không thể chịu đựng stale config: nếu scheduler đọc stale và tin rằng pod chưa được schedule trong khi thực ra đã được schedule rồi, nó sẽ schedule lại — dẫn đến duplicate pod. etcd chọn CP để loại trừ khả năng này hoàn toàn.

Cassandra vs etcd: vì sao một chọn AP, một chọn CP

Cây chọn công cụ phân tán theo bài toán đang gặp, năm câu hỏi dẫn tới sáu công cụ

Cassandra AP phù hợp khi:

  • Dữ liệu có thể được đọc lại (user preferences, activity log) — stale vài giây không gây hại.
  • Scale cực lớn (hàng trăm node, petabyte) — Raft không scale tốt ở số node này.
  • Cần geo-distribution với low latency writes ở mỗi datacenter.

etcd CP phù hợp khi:

  • Config, locking, service discovery — bất kỳ inconsistency nào đều gây hành vi sai.
  • Cluster nhỏ (thường 3-5 node) — Raft hoạt động tốt nhất ở quy mô này.
  • Đổi được availability lấy correctness.

Liên hệ các bài khác

  • Consistent hashing: token ring của Cassandra là ứng dụng trực tiếp — bài đó dạy cơ chế, case study này cho thấy cách Cassandra thêm RF và vnode vào.
  • Quorum: tunable consistency level (ONE/QUORUM/ALL) của Cassandra là R+W>N ở các mức khác nhau. Bài đó chứng minh R+W>N đảm bảo overlap.
  • Merkle tree: anti-entropy repair của Cassandra dùng Merkle tree để tìm đúng đoạn data bị chênh lệch mà không phải so toàn bộ.
  • Gossip: Cassandra gossip protocol là ứng dụng chuẩn của epidemic broadcast — membership, failure detection, load balancing info.
  • Raft: etcd là reference implementation Raft phổ biến nhất. Bài đó giải thích leader election và log replication — case study này cho thấy Kubernetes dùng nó ra sao.

Tự kiểm tra

Tự kiểm tra
0/5 câu đã trả lời
  1. Q1
    Cassandra dùng consistent hashing với RF=3. Khi Node B fail, Cassandra vẫn phục vụ key của Node B như thế nào?
  2. Q2
    Tại sao etcd từ chối phục vụ khi mất quorum (mất hơn N/2 node), trong khi Cassandra vẫn tiếp tục với consistency level ONE?
  3. Q3
    Cassandra dùng Merkle tree để làm gì trong anti-entropy repair? Tại sao không so sánh từng row trực tiếp?
  4. Q4
    Trong Raft của etcd, tại sao mọi write phải đi qua leader? Điều gì xảy ra nếu cho phép follower nhận write trực tiếp?
  5. Q5
    Gossip protocol của Cassandra chỉ trao đổi với 1-3 node mỗi giây. Tại sao thông tin vẫn lan khắp cluster 1 000 node trong vài giây?

Bài tiếp theo: Module 4 — Tổng kết & cheat sheet

Bài này đáng gửi cho bạn học cùng?

Copy link đã gắn nguồn — dán group, chat, hoặc LinkedIn.

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

Đặt 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

Bài tiếp theo

Module 4 — Tổng kết & cheat sheet