Multi-Leader & Leaderless Replication — Hai mô hình replication mở rộng (DDIA)
Mở đầu
Ảnh: ThisisEngineering — Pexels
Ở phần trước (Chương 5.1), chúng ta đã tìm hiểu về single-leader replication — mô hình có một leader duy nhất nhận writes, các followers sao chép dữ liệu từ leader. Đây là mô hình phổ biến nhất, nhưng không phải lúc nào cũng phù hợp.
Trong phần này, chúng ta sẽ khám phá hai mô hình replication khác: multi-leader replication (nhiều leader) và leaderless replication (không leader). Cả hai đều giải quyết những hạn chế của single-leader, nhưng kèm theo những đánh đổi thú vị về consistency và conflict resolution.
Multi-Leader Replication — Khi Có Nhiều Hơn Một Leader
Ảnh: Daniel — Pexels
Như tên gọi, multi-leader replication cho phép nhiều node trong cluster đều chấp nhận writes. Mỗi leader xử lý ghi và đồng bộ thay đổi đến tất cả các node khác (các leader khác và followers). Mỗi leader đồng thời cũng đóng vai trò là follower của các leader kia.
Use Cases thực tế
Multi-datacenter operations — Đây là use case phổ biến nhất. Bạn có datacenter ở Mỹ, Châu Âu và Châu Á. Mỗi datacenter có một leader riêng. User ở Châu Á ghi vào leader Asia, không cần chờ replication qua Mỹ. Việc này giảm latency viết đáng kể và chịu được lỗi datacenter — nếu một DC sập, các DC khác vẫn hoạt động.
Clients offline (offline-first) — Ứng dụng như CouchDB hoạt động local-first: mỗi thiết bị là một "leader" cục bộ, ghi vào database local. Khi có mạng, các thiết bị đồng bộ với nhau. Đây chính là multi-leader replication, với mỗi thiết bị là một leader và conflict xảy ra khi đồng bộ.
Collaborative editing — Google Docs, Notion, Figma — tất cả đều dùng biến thể của multi-leader replication. Mỗi người dùng là một leader, thay đổi được đồng bộ real-time qua server. Nếu hai người cùng sửa một đoạn văn, conflict xảy ra và cần được giải quyết.
Vấn đề Lớn Nhất: Write Conflicts
Khi chỉ có một leader, conflict là chuyện đơn giản — leader quyết định thứ tự writes, không có xung đột. Nhưng với multi-leader, hai leader có thể nhận cùng lúc hai writes khác nhau trên cùng một record. Khi đồng bộ, conflict xuất hiện.
Ví dụ: User A sửa tên sản phẩm thành "iPhone 14" trên leader US, cùng lúc User B sửa thành "iPhone 14 Pro" trên leader EU. Khi hai leader đồng bộ, ai đúng?
DDIA trình bày các chiến lược xử lý conflict:
- Conflict avoidance (ưu tiên hàng đầu) — Thiết kế ứng dụng sao cho mỗi record chỉ ghi vào một leader cố định. VD: tất cả writes cho user có user_id % N đi đến leader thứ N. Đây là cách đơn giản và hiệu quả nhất, tránh conflict hoàn toàn.
- Last Write Wins (LWW) — Dùng timestamp, write nào đến sau thì thắng. Đơn giản nhưng dễ mất dữ liệu — conflict resolution mang tính "phá huỷ" hơn là "gộp".
- Conflict-free Replicated Data Types (CRDTs) — Cấu trúc dữ liệu được thiết kế toán học để tự động merge mà không cần coordination. Dùng trong Riak, Redis CRDT, và các ứng dụng collaborative editing (Yjs, Automerge).
- Merge strategies do ứng dụng định nghĩa — Viết code custom để merge conflict. VD: với giỏ hàng online, merge: giữ cả hai phiên bản, không xoá món nào.
- Lưu conflict cho user tự giải quyết — Để conflict tồn tại trong database, user xem và chọn phiên bản đúng (như CouchDB làm).
Leaderless Replication — Dynamo-Style
Ảnh: Pixabay — Pexels
Amazon Dynamo (2007 paper) giới thiệu một mô hình replication hoàn toàn khác: không có leader. Bất kỳ replica nào cũng có thể chấp nhận writes từ client. Ý tưởng này ảnh hưởng lớn đến các database NoSQL như Cassandra, DynamoDB, Riak, và Voldemort.
Trong leaderless replication, client ghi đến tất cả N replicas (hoặc ít nhất W replicas), và đọc từ tất cả N replicas (hoặc ít nhất R replicas). N, W, R là các tham số quorum.
Quorum — W + R > N
Cốt lõi của leaderless replication là quorum. Nếu bạn có N replicas, yêu cầu R replicas xác nhận đọc và W replicas xác nhận ghi, thì miễn là W + R > N, bạn đảm bảo đọc được dữ liệu mới nhất.
Tại sao? Vì ít nhất một replica trong số R bạn đọc phải overlap với W replicas bạn ghi. Nếu không có overlap, bạn có thể đọc dữ liệu cũ.
Ví dụ: N = 3, W = 2, R = 2 → W + R = 4 > 3 → mỗi lần đọc chắc chắn gặp ít nhất 1 replica có dữ liệu mới.
Các database cho phép tuỳ chỉnh N, W, R linh hoạt:
- W = N, R = 1 — Tối ưu cho đọc nhanh, ghi chậm hơn (phải ghi toàn bộ replicas).
- W = 1, R = N — Tối ưu cho ghi nhanh, đọc chậm hơn.
- W = R = (N + 1) / 2 — Cân bằng.
Nhưng quorum không phải là "vàng" — DDIA chỉ ra rằng ngay cả với W + R > N, bạn vẫn có thể đọc dữ liệu cũ trong nhiều trường hợp (sloppy quorum, concurrent writes, clock skew...).
Sloppy Quorum & Hinted Handoff
Trong hệ thống thực tế, khi mất mạng, một số replicas không thể truy cập. Thay vì từ chối request, database có thể chấp nhận sloppy quorum: ghi vào bất kỳ N node nào còn sống, không nhất thiết là N node trong vòng bầu cử chính thức.
Ví dụ: Cassandra cluster 3 nodes, nhưng 1 node chết. Client vẫn ghi thành công với W = 2 (ghi vào 2 node còn lại, dù chỉ có 2 node alive thay vì 3). Node thứ ba được ghi hinted handoff — tạm thời lưu trên node neighbour, sau đó chuyển lại khi node chính online.
Sloppy quorum tăng availability nhưng không đảm bảo quorum truyền thống. W + R không overlap → có thể đọc dữ liệu cũ.
Read Repair & Anti-Entropy
Trong leaderless replication, replicas dần dần lệch nhau theo thời gian. Hai cơ chế sửa lỗi:
- Read repair — Khi đọc, client thấy một replica trả về dữ liệu cũ hơn, nó ghi đè phiên bản mới cho replica đó. Chỉ sửa được dữ liệu đang được đọc.
- Anti-entropy (Merkle trees) — Tiến trình nền so sánh dữ liệu giữa các replicas và sửa khác biệt. Cassandra dùng Merkle trees — cấu trúc hash tree cho phép so sánh nhanh một vùng dữ liệu, xác định chính xác phần nào khác nhau mà không cần truyền toàn bộ dữ liệu.
Giới Hạn của Quorum Consistency
DDIA cảnh báo rằng quorum consistency nghe có vẻ mạnh mẽ, nhưng có nhiều điều kiện biên:
- Sloppy quorum không đảm bảo overlap.
- Concurrent writes không có ordering rõ ràng → các replicas có thể kết thúc với giá trị khác nhau. Dynamo dùng vector clocks để tracking causality (sẽ nói ở chương 9).
- Read repair chỉ sửa khi được đọc — nếu dữ liệu không bao giờ được đọc, nó sẽ mãi lỗi thời.
- Write đồng thời từ nhiều clients trên cùng một key tạo race condition.
Kết luận: quorum cho bạn xác suất cao đọc được dữ liệu mới, nhưng không phải là đảm bảo tuyệt đối.
Multi-Leader vs Leaderless — So Sánh
Ảnh: ThisisEngineering — Pexels
| Tiêu chí | Multi-Leader | Leaderless |
|---|---|---|
| Conflict handling | Cần chiến lược merge (LWW, CRDT, custom) | Vector clocks / LWW |
| Write availability | Cao (nhiều leader, nếu 1 leader chết thì dùng leader khác) | Cao (ghi vào bất kỳ W nodes nào) |
| Read consistency | Tốt nếu dùng synchronous replication | Phụ thuộc vào quorum, read repair, anti-entropy |
| Complexity | Trung bình (conflict resolution) | Cao hơn (vector clocks, read repair, hinted handoff) |
| Use cases | Multi-DC, offline-first, collaborative editing | Cassandra, DynamoDB, Riak (high availability, low latency) |
Key Takeaways
- Multi-leader replication cho phép nhiều node nhận writes — hữu ích cho multi-datacenter và offline-first, nhưng cần chiến lược xử lý conflict.
- Leaderless replication (Dynamo-style) không có leader — client ghi/đọc trực tiếp đến N replicas với quorum W + R > N.
- Conflict resolution có nhiều mức độ: LWW đơn giản nhưng dễ mất dữ liệu, CRDTs mạnh mẽ nhưng phức tạp, custom merge linh hoạt nhất.
- Sloppy quorum + hinted handoff tăng availability nhưng giảm consistency guarantee.
- Quorum không phải là giải pháp hoàn hảo — các điều kiện biên (concurrent writes, sloppy quorum, clock skew) có thể phá vỡ đảm bảo.
Glossary
| Multi-leader replication | Mô hình với nhiều leader, mỗi leader nhận writes và đồng bộ đến các leader/follower khác |
| Leaderless replication | Mô hình không có leader, client ghi/đọc trực tiếp đến N replicas |
| Quorum | Số lượng replica tối thiểu cần xác nhận cho một thao tác ghi (W) hoặc đọc (R) để đảm bảo consistency |
| CRDT | Conflict-free Replicated Data Type — cấu trúc dữ liệu tự động merge không cần coordination |
| Read repair | Sửa dữ liệu cũ trên replica khi client đọc và phát hiện phiên bản lỗi thời |
| Merkle tree | Cấu trúc hash tree dùng trong anti-entropy để so sánh dữ liệu nhanh giữa các replicas |
| Sloppy quorum | Quorum mở rộng ghi vào nodes ngoài vòng chính thức khi replica bị down, tăng availability |
| Hinted handoff | Ghi tạm dữ liệu cho replica chết lên node neighbour, chuyển lại khi replica online |
| Conflict avoidance | Thiết kế ứng dụng sao cho mỗi record chỉ ghi vào một leader duy nhất, tránh conflict hoàn toàn |
Kết
Multi-leader và leaderless replication là hai mô hình mạnh mẽ cho những hệ thống cần high availability và low latency ở quy mô lớn. Không có mô hình nào "tốt nhất" — mỗi loại có điểm mạnh riêng và phù hợp với những use case khác nhau.
DDIA phân tích rất kỹ các điều kiện biên và cạm bẫy của từng mô hình, đặc biệt là các trường hợp quorum không hoạt động như mong đợi. Bài học lớn nhất: không có giải pháp replication nào là hoàn hảo — bạn luôn đánh đổi giữa consistency, availability, và performance.
Phần tiếp theo sẽ nói về Partitioning (Sharding) — cách chia dữ liệu ra nhiều node để scale ngang.