DDIA · Martin Kleppmann · Chương 5DDIA · Martin Kleppmann · Chương 5DDIA · Martin Kleppmann · Chapter 5
DDIA · Chương 5 · Nhân bảnDDIA · Chương 5 · Nhân bảnDDIA · Chapter 5 · Replication

Giữ cùng một dữ liệu trên nhiều máyChép dữ liệu ra nhiều máyKeeping the same data on several machines

Chương 5 mở ra Phần II của cuốn sách — phần dữ liệu phân tán. Kleppmann nêu ba lý do người ta chấp nhận giữ nhiều bản sao của cùng một tập dữ liệu: đặt dữ liệu gần người dùng về mặt địa lý để giảm độ trễ, giữ hệ còn chạy khi một phần chết, và rải tải đọc ra nhiều máy1. Nhưng ông lập tức chỉ ra chỗ khó thật: nếu dữ liệu không bao giờ đổi, nhân bản chỉ là chép một lần rồi thôi. Toàn bộ độ khó của nhân bản nằm ở chỗ xử lý thay đổi — và ba mươi năm nghiên cứu chỉ sinh ra ba họ lời giải: một lãnh đạo, nhiều lãnh đạo, và không lãnh đạo.

Nếu chỉ có một cuốn sổ ghi chép mà nó cháy mất thì coi như xong. Nên người ta chép thành nhiều cuốn, để ở nhiều nơi: một cuốn cháy vẫn còn cuốn khác, và ai ở gần cuốn nào thì đọc cuốn đó cho nhanh. Chép thì dễ. Khó là ở chỗ: khi có người sửa một cuốn, làm sao để mấy cuốn kia cũng được sửa theo cho khớp?

Chapter 5 opens Part II of the book — distributed data. Kleppmann gives three reasons people accept keeping multiple copies of the same dataset: putting data geographically close to users to cut latency, keeping the system running when parts of it die, and spreading read load across machines1. But he immediately points at the real difficulty: if the data never changed, replication would be a one-off copy. All the difficulty of replication lies in handling changes — and thirty years of research have produced only three families of answer: single-leader, multi-leader, and leaderless.

∴
Luận đề của chương Điều quan trọng nhất The thesis of the chapter

Nhân bản không phải bài toán sao chép, mà là bài toán quyết định ai được phép ghi và làm gì khi các bản sao bất đồng. Ba họ kiến trúc — single-leader, multi-leader, leaderless — không phải ba mức độ tốt dần, mà là ba cách đặt chi phí ở chỗ khác nhau: hoặc trả bằng độ sẵn sàng khi ghi, hoặc trả bằng xung đột phải tự hóa giải, hoặc trả bằng việc ứng dụng phải hiểu chuyện đồng thời. Không có lựa chọn nào miễn phí, và mọi “bảo đảm” trong chương này đều kèm điều kiện.

Chép sổ ra nhiều cuốn thì dễ. Câu hỏi thật là: ai được quyền viết vào sổ, và khi hai cuốn ghi khác nhau thì tin cuốn nào? Có ba cách trả lời, và cách nào cũng phải trả giá ở một chỗ nào đó. Nhớ nhé: không có cách chép sổ nào vừa nhanh, vừa luôn đúng, vừa không bao giờ hỏng.

Replication is not a copying problem but a problem of deciding who may write and what to do when copies disagree. The three families — single-leader, multi-leader, leaderless — are not three grades of quality but three ways of placing the cost somewhere else: you pay in write availability, or in conflicts you must resolve yourself, or in an application that must understand concurrency. None is free, and every “guarantee” in this chapter comes with conditions.

Trục 1 · Leaders and followersAxis 1 · Leaders and followers

Một người ghi, những người còn lại chép theoOne writer, everyone else replays the log

Lời giải phổ biến nhất — và cũng là lời giải mà PostgreSQL, MySQL, MongoDB và phần lớn cơ sở dữ liệu quan hệ dùng — là nhân bản dựa trên lãnh đạo: một bản sao được chỉ định làm leader, mọi lệnh ghi của client đều đi qua nó; leader ghi thay đổi vào log nhân bản rồi đẩy log ấy sang các follower, mỗi follower phát lại log theo đúng thứ tự cũ1. Client có thể đọc từ leader hoặc bất kỳ follower nào, nhưng chỉ leader mới nhận ghi. Toàn bộ phần còn lại của trục này là giá phải trả cho cái sắp xếp gọn gàng đó: chờ hay không chờ follower, dựng follower mới thế nào, và chuyện gì xảy ra khi chính leader chết.

Tưởng tượng một lớp học chép chính tả: cô giáo đọc, cả lớp chép. Chỉ mình cô được quyết định nội dung; các bạn chỉ chép lại đúng thứ tự cô đọc. Muốn xem bài thì xem vở ai cũng được, nhưng muốn sửa bài thì phải qua cô. Cách này gọn — nhưng nếu cô giáo nghỉ ốm thì cả lớp phải chọn ai đọc thay, và đó mới là chỗ rắc rối.

The most common answer — the one PostgreSQL, MySQL, MongoDB and most relational databases use — is leader-based replication: one replica is designated the leader, all client writes go through it; the leader writes each change to a replication log and ships it to the followers, each of which replays the log in the same order1. Clients may read from the leader or from any follower, but only the leader accepts writes. Everything else on this axis is the price of that tidy arrangement: whether to wait for followers, how to build a new one, and what happens when the leader itself dies.

Lý do 01 · LatencyReason 01 · Latency

Đặt dữ liệu gần người dùngPut data near the user

Ánh sáng cũng cần thời gian để đi vòng quanh Trái Đất. Giữ một bản sao ở trung tâm dữ liệu gần người dùng nhất là cách duy nhất cắt được phần độ trễ do khoảng cách vật lý gây ra1 — không tối ưu truy vấn nào bù được cho nửa vòng địa cầu.

Muốn mượn sách nhanh thì thư viện phải ở gần nhà, chứ không phải ở tỉnh khác. Dữ liệu cũng vậy. Nhớ nhé: ở gần thì nhanh hơn.

Even light takes time to cross the planet. Keeping a copy in the data centre nearest the user is the only way to remove the latency that distance itself imposes1 — no query optimisation compensates for half a globe.

Gần về địa lý = nhanh về độ trễ.
Nhớ nhé: gần thì nhanh.
Geographic closeness buys latency.
Lý do 02 · AvailabilityReason 02 · Availability

Vẫn chạy khi một phần chếtKeep working when parts die

Có nhiều bản sao nghĩa là mất một máy không đồng nghĩa mất dữ liệu hay mất dịch vụ1. Đây chính là dung thứ lỗi của Chương 1 được hiện thực ở tầng dữ liệu — và cũng là lý do nhân bản khác hẳn sao lưu: bản sao ở đây đang phục vụ, không nằm chờ trong kho.

Có hai cuốn sổ thì một cuốn ướt mưa vẫn còn cuốn kia đọc được. Nhớ nhé: nhiều bản thì bớt sợ mất.

Having several copies means losing one machine doesn't mean losing the data or the service1. This is Chapter 1's fault tolerance realised at the data layer — and it's why replication differs from backup: these copies are serving traffic, not sitting in storage.

Mất một nút ≠ mất dịch vụ.
Nhớ nhé: còn bản dự phòng.
One node lost ≠ service lost.
Lý do 03 · Read throughputReason 03 · Read throughput

Rải tải đọc ra nhiều máySpread read load across machines

Vì mọi follower đều có đủ dữ liệu, ta có thể rót truy vấn đọc vào chúng — kiến trúc read-scaling mà Kleppmann mô tả cho các ứng dụng đọc nhiều, ghi ít1. Nhưng nó chỉ mở rộng phần đọc: mọi lệnh ghi vẫn dồn về một leader duy nhất.

Nhiều cuốn sổ thì nhiều bạn cùng đọc một lúc được, khỏi phải xếp hàng. Nhưng muốn viết thì vẫn phải chờ tới lượt ở cuốn chính. Nhớ nhé: chép nhiều bản giúp đọc nhanh, không giúp ghi nhanh.

Because every follower holds the full dataset, read queries can be poured into them — the read-scaling architecture Kleppmann describes for read-heavy, write-light workloads1. But it only scales reads: every write still funnels through a single leader.

Mở rộng đọc, không mở rộng ghi.
Nhớ nhé: nhanh đọc, không nhanh ghi.
Scales reads, not writes.
Leader-based replication: the whole cost is in the log

Trong mô hình một-lãnh-đạo, thứ thật sự đi qua mạng không phải dữ liệu mà là một dòng thay đổi có thứ tự. Follower không “đồng bộ” với leader theo nghĩa so sánh trạng thái; nó chỉ phát lại đúng chuỗi thay đổi ấy. Vì thế mọi câu hỏi khó của trục này đều là câu hỏi về log: leader có chờ log tới nơi rồi mới báo thành công không, một follower mới bắt đầu phát lại từ vị trí nào, và khi leader chết thì phần log chưa kịp đi đâu về đâu.

Cô giáo không đưa vở của mình cho cả lớp chép lại từ đầu. Cô đọc từng câu theo thứ tự, ai cũng chép đúng thứ tự đó nên cuối buổi vở ai cũng giống nhau. Nhớ nhé: quan trọng là chép đúng thứ tự, không phải chép nhanh.

In the single-leader model, what actually crosses the network is not data but an ordered stream of changes. A follower doesn't “sync” with the leader by comparing state; it replays that exact sequence. So every hard question on this axis is a question about the log: does the leader wait for the log to arrive before reporting success, where does a new follower start replaying, and what becomes of log entries that never left a leader that has died.

Điều này giải thích vì sao log nhân bản lại là một trong những trừu tượng có sức sống nhất trong hệ dữ liệu. Cùng một dòng thay đổi có thứ tự ấy nuôi được cả bản sao chờ sẵn (standby), cả chỉ mục tìm kiếm, cả kho dữ liệu phân tích, cả cache — miễn là consumer phát lại đúng thứ tự. PostgreSQL công khai hóa chính ý tưởng này qua logical decoding: giải mã WAL nội bộ thành một dòng thay đổi mức hàng mà bên ngoài tiêu thụ được10. Và không phải mọi kiến trúc nhân bản đều hình sao quanh một leader: chain replication xếp các bản sao thành một chuỗi, ghi vào đầu chuỗi và đọc ở cuối chuỗi, để đạt vừa thông lượng cao vừa tính nhất quán mạnh25 — Kleppmann nhắc nó như một biến thể đáng biết của họ leader-based.

Ba lý do Gần người dùng (độ trễ), chịu lỗi (sẵn sàng), rải tải đọc. Ghi vẫn dồn về một chỗ.
Đồng bộ hay không Chờ follower thì bền nhưng dễ treo; không chờ thì nhanh nhưng có thể mất ghi khi leader chết.
Failover là chỗ đau Thăng chức một follower là thao tác nguy hiểm nhất: mất ghi, split brain, chọn timeout.
Vì sao chép nhiều bản Để ở gần cho nhanh, để không mất, và để nhiều người cùng đọc.
Chờ hay không chờ Chờ bạn chép xong thì chắc chắn hơn nhưng chậm; không chờ thì nhanh mà dễ sót.
Cô giáo nghỉ ốm Chọn ai đọc thay là lúc dễ rối nhất của cả lớp.
Three reasons Closeness (latency), fault tolerance (availability), read spreading. Writes still funnel to one place.
Sync or not Waiting for a follower is durable but stall-prone; not waiting is fast but can lose writes when the leader dies.
Failover is the wound Promoting a follower is the riskiest operation: lost writes, split brain, timeout choice.
  1. Synchronous vs asynchronous

    Chờ follower, hay báo thành công ngay?Wait for the follower, or ack immediately?

    Với nhân bản đồng bộ, leader chỉ báo cho client là ghi thành công sau khi follower xác nhận đã nhận. Ưu điểm: follower chắc chắn có bản sao cập nhật. Nhược điểm chí mạng mà Kleppmann nhấn: nếu follower không phản hồi — vì sập, vì mạng, vì quá tải — thì ghi bị chặn lại toàn bộ1. Vì thế trong thực tế hiếm khi mọi follower đều đồng bộ; cấu hình hay gặp là semi-synchronous: một follower đồng bộ, số còn lại bất đồng bộ. PostgreSQL mặc định nhân bản luồng ở chế độ bất đồng bộ, và chỉ chuyển sang đồng bộ khi người vận hành đặt synchronous_standby_names9; MySQL cung cấp nhân bản bán-đồng-bộ như một plugin phải bật riêng12.

    Cô giáo đọc một câu rồi hỏi: “Bạn chép xong chưa?” — chờ trả lời rồi mới đọc câu tiếp. Chắc chắn không ai sót, nhưng nếu một bạn ngủ gật thì cả lớp đứng im. Cách kia là cô cứ đọc tiếp, ai chậm thì đuổi theo sau — nhanh hơn, nhưng bạn chậm có thể sót mất mấy câu. Nhớ nhé: chờ thì chắc mà chậm, không chờ thì nhanh mà dễ sót.

    With synchronous replication the leader reports success to the client only after a follower confirms receipt. The upside: that follower certainly has an up-to-date copy. The fatal downside Kleppmann stresses: if the follower doesn't answer — crash, network, overload — writes block entirely1. So in practice it's rare for all followers to be synchronous; the common setup is semi-synchronous: one synchronous follower, the rest asynchronous. PostgreSQL streams asynchronously by default and only becomes synchronous when an operator sets synchronous_standby_names9; MySQL ships semisynchronous replication as a plugin you must enable12.

    Đọc thế nào“Đồng bộ” không phải nút bật/tắt của cả hệ mà là một lựa chọn trên từng follower. Hỏi: có bao nhiêu bản sao thật sự đồng bộ, và nếu bản duy nhất ấy chậm thì hệ ghi có đứng không?
    Hỏi gìCó ai được chờ không, và nếu người đó ngủ gật thì cả lớp có phải dừng không?
    How to read it“Synchronous” is not a system-wide switch but a choice per follower. Ask: how many replicas are genuinely synchronous, and if the only one is slow, do writes stall?

    Bán-đồng-bộ là thỏa hiệp có ý đồ

    Semi-synchronous giữ được lời hứa “luôn có ít nhất hai nút mang dữ liệu cập nhật” mà không để một follower chậm khóa cả hệ: nếu follower đồng bộ ngừng phản hồi, một follower bất đồng bộ được nâng lên thay vai đồng bộ. Đây là lý do PostgreSQL tách riêng synchronous_commit thành nhiều mức — remote_write, on, remote_apply — mỗi mức mua một lượng bền vững khác nhau bằng một lượng độ trễ khác nhau9.

    Bất đồng bộ không phải là sai

    Kleppmann không coi bất đồng bộ là lựa chọn tồi: nó là lựa chọn thường dùng, đặc biệt khi có nhiều follower hoặc follower ở xa về địa lý. Cái giá được nói thẳng: nếu leader chết trước khi log kịp đi, những ghi đã báo thành công cho client có thể mất hẳn. Đó là một đánh đổi bền-vững-đổi-lấy-hiệu-năng, phải được nói rõ chứ không nên giấu sau chữ “high availability”.

  2. Setting up new followers

    Dựng follower mới: ảnh chụp + vị trí logNew followers: snapshot plus log position

    Thêm một bản sao không thể làm bằng cách chép file dữ liệu đang chạy — dữ liệu luôn đổi giữa chừng, và bản chép sẽ không nhất quán. Quy trình mà Kleppmann mô tả gồm bốn bước: chụp một snapshot nhất quán của leader (thường không cần khóa toàn bộ); chép snapshot sang nút mới; nút mới kết nối tới leader và xin toàn bộ thay đổi kể từ đúng vị trí log gắn với snapshot đó; phát lại cho tới khi bắt kịp1. PostgreSQL gọi vị trí ấy là log sequence number9.

    Bạn đi học muộn. Không thể chép vở của bạn bên cạnh rồi ngồi im — vì cô vẫn đang đọc tiếp. Bạn phải: mượn vở chép tới câu số 30, rồi hỏi cho rõ cô đang đọc tới câu bao nhiêu, và chép tiếp từ đúng chỗ đó. Nhớ nhé: chép xong phải biết mình đang ở câu nào, không thì lệch mất.

    Adding a replica can't be done by copying the live data files — data changes mid-copy and the result is inconsistent. Kleppmann's procedure has four steps: take a consistent snapshot of the leader (usually without locking the whole database); copy it to the new node; have the new node connect to the leader and request all changes since the exact log position associated with that snapshot; replay until caught up1. PostgreSQL calls that position a log sequence number9.

    Vì sao quan trọngChính cặp “snapshot + vị trí log” là thứ làm cho việc thêm bản sao trở nên an toàn và lặp lại được. Không có nó, bạn chỉ có một bản chép mờ mịt không biết đang đứng ở đâu trong dòng lịch sử.
    Ý chínhChép tới đâu thì phải nhớ số câu tới đó, rồi chép tiếp từ đúng chỗ.
    Why it mattersThe “snapshot plus log position” pair is exactly what makes adding a replica safe and repeatable. Without it you have a blurry copy with no idea where it stands in history.

    Vì sao phải giữ log

    Nút mới chỉ bắt kịp được nếu leader còn giữ đủ phần log kể từ vị trí snapshot. PostgreSQL đề cập thẳng nguy cơ này: nếu dùng nhân bản luồng mà không lưu trữ WAL, máy chủ có thể tái sử dụng các phân đoạn WAL cũ trước khi standby kịp nhận, và khi đó standby phải khởi tạo lại từ một base backup mới — tránh bằng wal_keep_size hoặc replication slot9.

    Bắt kịp là trạng thái, không phải sự kiện

    “Bắt kịp” không phải một khoảnh khắc rồi xong: follower ở trong trạng thái đuổi theo vĩnh viễn, độ trễ dao động theo tải. Chính khoảng cách dao động này là replication lag — chủ đề của trục 2 — và nó tồn tại cả khi mọi thứ đang hoạt động bình thường.

  3. Follower failure: catch-up recovery

    Follower chết: đuổi theo phần đã lỡFollower down: catch-up recovery

    Trường hợp dễ là follower chết. Vì mỗi follower giữ trên đĩa nhật ký các thay đổi nó đã nhận, sau khi khởi động lại nó biết giao dịch cuối cùng mình đã xử lý, kết nối lại với leader và xin mọi thay đổi phát sinh trong lúc mình vắng mặt — Kleppmann gọi đó là catch-up recovery1. Không cần thao tác thủ công, không có quyết định nguy hiểm nào phải ra.

    Một bạn đi vệ sinh rồi quay lại. Bạn ấy biết mình chép tới câu 12, nên chỉ cần hỏi mượn phần từ câu 13 trở đi rồi chép bù. Nhớ nhé: nhớ mình dừng ở đâu thì quay lại rất dễ.

    The easy case is a failed follower. Because each follower keeps on disk a log of the changes it has received, after restarting it knows the last transaction it processed, reconnects to the leader, and requests every change that happened while it was away — Kleppmann's catch-up recovery1. No manual intervention, no dangerous decision to make.

    Hệ quả thực tếMất một follower gần như không phải sự cố — nó là chuyện thường ngày, miễn là leader còn giữ đủ log. Điều đáng lo không phải follower chết, mà leader chết.
    Yên tâmMột bạn vắng chút xíu rồi chép bù là chuyện bình thường.
    The practical upshotLosing a follower is barely an incident — it's routine, provided the leader still holds enough log. What should worry you is not a dead follower but a dead leader.
  4. Leader failure: failover

    Leader chết: failover và bốn cái bẫyLeader down: failover and its four traps

    Khi leader chết, một follower phải được thăng chức: client phải được cấu hình lại để gửi ghi tới nó, các follower khác phải bắt đầu lấy log từ nó. Kleppmann liệt kê những chỗ dễ hỏng1. Một: với nhân bản bất đồng bộ, leader mới có thể thiếu phần ghi cuối của leader cũ — cách xử lý phổ biến nhất là vứt bỏ phần ghi đó, làm hỏng mọi cam kết bền vững đã hứa với client. Hai: vứt ghi trở nên nguy hiểm khi có hệ khác ngoài cơ sở dữ liệu phụ thuộc vào nội dung bị vứt. Ba: split brain — hai nút cùng tin mình là leader và cùng nhận ghi. Bốn: chọn timeout bao lâu thì tuyên bố leader đã chết — quá dài thì kéo dài gián đoạn, quá ngắn thì gây failover thừa, khiến một hệ đang chỉ tải cao bỗng rơi vào vòng xoáy chuyển vai liên tục.

    Cô giáo đột ngột phải về. Lớp phải chọn một bạn đọc thay. Rắc rối là: bạn đó chưa chắc nghe kịp mấy câu cuối cô vừa đọc; hai bạn có thể cùng tưởng mình được chọn rồi đọc hai kiểu khác nhau; và nếu cô chỉ ra ngoài một phút mà lớp đã vội thay người thì càng loạn. Nhớ nhé: đổi người cầm trịch là lúc dễ sai nhất, chứ không phải lúc bình thường.

    When a leader dies, a follower must be promoted: clients must be reconfigured to send writes to it, and other followers must start consuming its log. Kleppmann lists the places this goes wrong1. One: with asynchronous replication the new leader may be missing the old leader's last writes — and the most common remedy is to discard them, breaking the durability already promised to clients. Two: discarding becomes dangerous when systems outside the database depend on the discarded content. Three: split brain — two nodes both believing they are leader and both accepting writes. Four: choosing the timeout before declaring a leader dead — too long prolongs the outage, too short triggers needless failovers, so a merely overloaded system tumbles into a cascade of role changes.

    Dùng khi nàoTrước khi bật failover tự động, hãy trả lời được: bạn chấp nhận mất bao nhiêu ghi, bạn chặn split brain bằng cơ chế nào, và timeout của bạn dựa trên số đo nào chứ không phải cảm giác.
    Hỏi gìĐổi người cầm trịch thì mất mấy câu cuối? Làm sao để không có hai người cùng đọc?
    When to useBefore enabling automatic failover, be able to answer: how many writes you accept losing, by what mechanism you prevent split brain, and what measurement — not what feeling — your timeout rests on.

    Một sự cố có thật

    Kleppmann dẫn sự cố GitHub tháng 9 năm 2012 như minh họa cho cái bẫy thứ hai3. Báo cáo công khai của GitHub mô tả: một lỗi segfault của Pacemaker làm cụm bị phân mảnh trạng thái, hai cuộc bầu leader diễn ra không phối hợp, và nút được bầu lại chính là nút mà đội vận hành đã xác định là lỗi thời; hậu quả là dữ liệu trôi lệch, và vì GitHub dùng khóa ngoại giữa id do MySQL sinh và dữ liệu trong Redis, các quan hệ giữa hai kho dữ liệu mất đồng bộ — một số sự kiện hiện sai bảng tin, và trong bảy phút, 16 kho riêng tư bị định tuyến sai21.

    Vì sao không có lời giải dễ

    Split brain thường được chặn bằng cách buộc tắt nút thừa (kỹ thuật quen gọi là fencing / STONITH), nhưng Kleppmann lưu ý rằng nếu cơ chế này thiết kế cẩu thả thì cả hai nút đều có thể bị tắt. Ông kết lại rằng chính vì những vấn đề này mà một số đội vận hành cố tình chọn failover thủ công. Đây cũng là lý do các chương sau — về giao dịch phân tán và đồng thuận — tồn tại: chúng là câu trả lời nghiêm túc cho đúng những câu hỏi mà failover đặt ra.

  5. Implementation of replication logs

    Bốn kiểu log nhân bảnFour ways to build a replication log

    “Log nhân bản” không phải một thứ duy nhất; Kleppmann trình bày bốn cách hiện thực, mỗi cách hỏng theo một kiểu riêng1. Statement-based gửi chính câu lệnh ghi — gọn nhưng vỡ khi câu lệnh không tất định (NOW(), RAND()), khi có cột tự tăng, hay khi có side effect. WAL shipping gửi nguyên nhật ký ghi-trước mức byte — chính xác nhưng gắn chặt vào định dạng lưu trữ, nên rất khó nâng cấp phiên bản mà không dừng dịch vụ. Logical (row-based) gửi mô tả thay đổi ở mức hàng, tách khỏi nội bộ động cơ lưu trữ — nhờ đó leader và follower có thể chạy phiên bản khác nhau, và hệ ngoài có thể tiêu thụ được. Trigger-based đẩy việc lên tầng ứng dụng bằng trigger — linh hoạt nhất, nhưng nhiều lỗi và tốn kém nhất.

    Có bốn cách để “kể lại” một thay đổi. Một: đọc lại nguyên câu lệnh (“cộng thêm 1 điểm cho bạn nào ngồi bàn đầu”) — nhưng nếu hôm nay bàn đầu ngồi khác thì kết quả ra khác. Hai: chép từng nét chữ y hệt — chính xác nhưng phải cùng loại vở. Ba: nói rõ kết quả (“ô điểm của Lan đổi từ 7 thành 8”) — cách này ai cũng hiểu được. Bốn: nhờ một bạn ngồi ghi hộ theo cách riêng. Nhớ nhé: kể lại kết quả thì an toàn hơn kể lại câu lệnh.

    A “replication log” is not one thing; Kleppmann presents four implementations, each broken in its own way1. Statement-based ships the write statements themselves — compact, but it breaks on nondeterminism (NOW(), RAND()), auto-increment columns and side effects. WAL shipping ships the byte-level write-ahead log — exact, but coupled to the storage format, which makes zero-downtime version upgrades very hard. Logical (row-based) ships a row-level description of the change, decoupled from storage internals — so leader and follower can run different versions, and external systems can consume it. Trigger-based pushes the work up into the application via triggers — the most flexible, and the most error-prone and expensive.

    Đọc thế nàoCách bạn nhân bản quyết định bạn nâng cấp được tới đâu. Log mức hàng là thứ mở cửa cho change data capture: cùng một dòng đó nuôi chỉ mục tìm kiếm, cache và kho phân tích — chủ đề Kleppmann quay lại ở Chương 11.
    Ý chínhKể lại kết quả cụ thể thì ai nghe cũng hiểu, kể lại mệnh lệnh thì dễ ra kết quả khác.
    How to read itHow you replicate decides how far you can upgrade. A row-level log is what opens the door to change data capture: the same stream feeds search indexes, caches and analytics — a thread Kleppmann picks up again in Chapter 11.

    Trong sản phẩm thật

    MySQL hiện đặt binlog_format mặc định là ROW và mô tả ba định dạng — statement-based, row-based, mixed — cùng những hạn chế của từng cái11. PostgreSQL đi theo hướng khác: nhân bản vật lý truyền WAL, còn logical decoding giải mã WAL thành dòng thay đổi mức hàng cho những consumer bên ngoài10. Hai lộ trình khác nhau dẫn tới cùng một kết luận của sách: log mức logic là cái tách được nhân bản khỏi nội bộ động cơ lưu trữ.

    Vì sao trigger-based vẫn tồn tại

    Vì đôi khi bạn cần nhân bản một phần dữ liệu, nhân bản sang một loại cơ sở dữ liệu khác, hoặc cần logic hóa giải xung đột riêng — những thứ mà log dựng sẵn của động cơ không làm được. Kleppmann nói thẳng cái giá: overhead cao hơn và nhiều lỗi hơn so với nhân bản dựng sẵn, nên đây là lựa chọn khi bạn thật sự cần sự linh hoạt đó, không phải lựa chọn mặc định.

Trục 2 · Problems with replication lagAxis 2 · Problems with replication lag

Độ trễ nhân bản: ba bảo đảm vá ba lỗ hổngReplication lag: three guarantees for three holes

Nếu bạn đọc từ follower để rải tải, bạn đang mua tốc độ bằng dữ liệu có thể cũ. Kleppmann gọi tình trạng này là eventual consistency — nếu ngừng ghi và chờ đủ lâu, mọi follower rốt cuộc sẽ hội tụ về cùng một giá trị1. Chỗ mà ông gõ mạnh: chữ “rốt cuộc” (eventually) cố tình không nêu giới hạn thời gian nào. Werner Vogels — người đặt khái niệm này vào trung tâm kiến trúc của Amazon — cũng mô tả nó theo đúng cách đó: hệ chỉ bảo đảm rằng nếu không có cập nhật mới nào, cuối cùng mọi truy cập sẽ trả về giá trị cập nhật gần nhất23. Trong hệ bình thường, độ trễ có thể là một phần giây; khi gần chạm giới hạn năng lực, nó có thể là vài giây hoặc vài phút.

Bạn nhờ bạn cùng bàn chép hộ một câu vào vở. Rồi bạn nhìn vào vở bạn ấy — nhưng bạn ấy chưa chép xong! Bạn hoảng: “Câu của mình biến mất rồi!” Thật ra không mất, chỉ là chưa tới. Cái khoảng “chưa tới” đó chính là điều làm người ta bối rối nhất khi có nhiều cuốn sổ.

If you read from followers to spread load, you are buying speed with possibly stale data. Kleppmann calls this eventual consistency — stop writing, wait long enough, and every follower will converge on the same value1. What he hammers: the word “eventually” deliberately names no time bound. Werner Vogels, who put the idea at the centre of Amazon's architecture, describes it the same way: the system merely guarantees that if no new updates are made, eventually all accesses will return the last updated value23. On a healthy system the lag may be a fraction of a second; near capacity it may be seconds or minutes.

Three anomalies, three guarantees, in order of pain

Kleppmann không liệt kê mọi sai lệch có thể xảy ra; ông chọn ba kiểu nghịch lý mà người dùng thật sự nhìn thấy, và với mỗi kiểu nêu một bảo đảm đúng mức để chữa nó. Điểm quan trọng: đây không phải ba mức mạnh dần của cùng một thứ, mà là ba lỗ hổng khác nhau — vá lỗ này không tự động vá lỗ kia, và hệ của bạn có thể cần một, hai, hoặc cả ba.

Có ba kiểu “chuyện lạ” xảy ra khi vở chép chậm: tự mình viết mà không thấy chữ mình; lúc thấy lúc không thấy; và câu trả lời hiện ra trước câu hỏi. Mỗi kiểu có một cách chữa riêng. Nhớ nhé: chữa được chuyện này chưa chắc chữa được chuyện kia.

Kleppmann doesn't enumerate every possible anomaly; he picks three that users actually see, and for each names a guarantee sized to fix it. The key point: these are not three strengths of one thing but three distinct holes — patching one does not patch another, and your system may need one, two or all three.

Ba bảo đảm này không phải phát minh của cuốn sách. Chúng đến từ bài báo năm 1994 của Douglas Terry và cộng sự tại Xerox PARC, đề xuất bốn session guarantee — Read Your Writes, Monotonic Reads, Writes Follow Reads, Monotonic Writes — cho dữ liệu nhân bản yếu nhất quán, với chủ ý trình cho ứng dụng một khung nhìn nhất quán với chính hành động của nó, ngay cả khi nó đọc và ghi từ nhiều máy chủ có thể không nhất quán với nhau8. Đây là một chi tiết đáng nhớ về mặt tư tưởng: các bảo đảm này gắn vào phiên làm việc của một người dùng, không phải vào trạng thái toàn cục của cơ sở dữ liệu. Chúng rẻ hơn nhất quán mạnh rất nhiều, chính vì chúng hứa ít hơn rất nhiều.

Read-your-writes Tự mình ghi thì tự mình thấy. Không hứa gì về ghi của người khác.
Monotonic reads Đã thấy rồi thì không được thấy mất. Thời gian không đi lùi.
Consistent prefix Ghi có quan hệ nhân quả phải xuất hiện đúng thứ tự nhân quả.
Thấy chữ mình Vừa viết xong thì phải nhìn thấy, không được biến mất.
Không quay ngược Đã thấy rồi thì lần sau không được mất đi.
Hỏi trước, đáp sau Không được thấy câu trả lời trước khi thấy câu hỏi.
Read-your-writes You see your own writes. It promises nothing about other people's.
Monotonic reads Once seen, never unseen. Time does not run backwards.
Consistent prefix Causally related writes appear in causal order.
  1. Reading your own writes

    Ghi rồi mà không thấy chữ của chính mìnhWriting, then not seeing your own write

    Người dùng gửi một bình luận; lệnh ghi đi tới leader; rồi trang được tải lại và truy vấn rơi vào một follower chưa kịp nhận. Với người dùng, bình luận vừa biến mất. Bảo đảm cần ở đây là read-after-write consistency, còn gọi là read-your-writes: bảo đảm rằng người dùng luôn nhìn thấy những cập nhật do chính họ gửi lên — và chỉ thế thôi, không nói gì về cập nhật của người khác1. Cách hiện thực đơn giản nhất: khi đọc thứ gì đó mà người dùng có thể vừa sửa, hãy đọc từ leader; những thứ khác đọc từ follower.

    Bạn viết tên mình lên bảng rồi quay đi. Lúc quay lại nhìn vào ảnh chụp cũ của bảng, tên bạn chưa có ở đó. Bạn tưởng mình chưa viết. Cách chữa đơn giản nhất: những gì chính bạn vừa viết thì phải nhìn vào bảng thật, chứ đừng nhìn ảnh cũ. Nhớ nhé: chữ của mình thì xem bản gốc.

    A user posts a comment; the write goes to the leader; the page reloads and the query lands on a follower that hasn't received it. To the user, the comment has vanished. The guarantee needed is read-after-write consistency, also called read-your-writes: a promise that a user always sees the updates they themselves submitted — and nothing more, saying nothing about other users' updates1. The simplest implementation: when reading something the user may have modified, read it from the leader; read everything else from followers.

    Dùng khi nàoBất cứ giao diện nào mà người dùng vừa sửa xong là nhìn lại ngay: hồ sơ cá nhân, bình luận, giỏ hàng, bài đăng nháp. Đây là bảo đảm rẻ nhất và cũng là bảo đảm bạn sẽ thiếu đầu tiên nếu chỉ chăm chăm rải tải đọc.
    Dùng saoCái gì mình vừa sửa thì đọc lại từ chỗ gốc.
    When to useAny screen where a user edits then immediately looks again: profiles, comments, carts, drafts. It's the cheapest guarantee, and the first one you'll be missing if you only think about spreading read load.

    Vì sao “đọc từ leader” không đủ lâu dài

    Nếu phần lớn dữ liệu đều có thể bị người dùng sửa, quy tắc “sửa thì đọc từ leader” kéo gần hết tải đọc về lại leader, xóa sạch lợi ích của việc rải tải. Kleppmann nêu các cách tinh vi hơn: theo dõi thời điểm cập nhật gần nhất của người dùng và trong khoảng một phút sau đó thì đọc từ leader; hoặc client nhớ dấu thời gian logic của ghi gần nhất rồi buộc bản sao phục vụ nó phải đã bắt kịp ít nhất tới mốc ấy.

    Nhiều thiết bị làm mọi thứ khó hơn

    Khi cùng một người dùng thao tác trên điện thoại rồi nhìn trên máy tính — Kleppmann gọi là cross-device read-after-write consistency — siêu dữ liệu về “ghi gần nhất” phải được tập trung ở nơi cả hai thiết bị đọc được, và hai thiết bị có thể đi qua hai trung tâm dữ liệu khác nhau, nên có thể phải định tuyến cả hai về cùng một trung tâm. Bảo đảm càng gắn với người thay vì với phiên, chi phí hạ tầng càng tăng.

  2. Monotonic reads

    Thấy rồi lại mất: thời gian đi lùiSeeing something, then unseeing it

    Đọc lần đầu rơi vào follower đã bắt kịp và thấy bình luận mới; đọc lần sau rơi vào follower tụt hậu và bình luận biến mất. Người dùng vừa đi ngược thời gian. Monotonic reads là bảo đảm rằng chuyện này không xảy ra — Kleppmann xếp nó là bảo đảm yếu hơn nhất quán mạnh nhưng mạnh hơn nhất quán rốt cuộc1. Cách đạt được thường rất thô sơ mà hiệu quả: đảm bảo mỗi người dùng luôn đọc từ cùng một bản sao, chọn theo băm của định danh người dùng chứ không phải ngẫu nhiên.

    Bạn nhìn vào vở của bạn A, thấy đã có câu 20. Lát sau bạn nhìn vào vở bạn B, bạn B mới chép tới câu 15 — câu 20 “biến mất”. Cách chữa: từ đầu tới cuối chỉ nhìn vở của một bạn thôi. Nhớ nhé: đừng nhảy qua nhảy lại giữa nhiều cuốn sổ chép chậm khác nhau.

    The first read lands on a caught-up follower and shows the new comment; the next read lands on a lagging one and the comment is gone. The user has just travelled backwards in time. Monotonic reads is the guarantee that this doesn't happen — Kleppmann ranks it as weaker than strong consistency but stronger than eventual consistency1. The usual implementation is crude but effective: make sure each user always reads from the same replica, chosen by a hash of their user ID rather than at random.

    Hệ quả thực tếNếu load balancer của bạn rải truy vấn đọc ngẫu nhiên qua các bản sao, bạn đang tự chuốc lấy nghịch lý này. “Dính” người dùng vào một bản sao là lời giải rẻ — nhưng nhớ rằng khi bản sao đó chết, người dùng phải được chuyển sang bản khác và bảo đảm tạm thời đứt.
    Ý chínhMỗi người chỉ nên nhìn vào một cuốn sổ cố định.
    The practical upshotIf your load balancer spreads reads randomly across replicas, you are inviting this anomaly. Pinning a user to one replica is the cheap answer — but remember that when that replica dies the user must be moved and the guarantee briefly breaks.
  3. Consistent prefix reads

    Câu trả lời đến trước câu hỏiThe answer arriving before the question

    Nghịch lý thứ ba vi phạm chính quan hệ nhân quả. Nếu một chuỗi ghi có quan hệ nhân quả với nhau được người khác đọc thấy theo thứ tự đảo ngược, cuộc hội thoại trở nên vô nghĩa. Consistent prefix reads là bảo đảm rằng ai đọc một chuỗi ghi như vậy cũng sẽ thấy chúng theo đúng thứ tự chúng đã xảy ra1. Kleppmann nói rõ đây là vấn đề đặc biệt của cơ sở dữ liệu phân mảnh: nếu các phần dữ liệu khác nhau nằm trên các phân mảnh khác nhau và mỗi phân mảnh nhân bản độc lập, thì không tồn tại thứ tự ghi toàn cục nào để mà tuân theo.

    Có người hỏi “Mấy giờ rồi?” và người kia đáp “Ba giờ.” Nếu bạn nghe câu đáp trước câu hỏi, bạn sẽ thấy rất kỳ. Chuyện đó xảy ra khi câu hỏi và câu đáp được chép vào hai cuốn sổ khác nhau, mà một cuốn chép nhanh hơn cuốn kia. Nhớ nhé: chuyện gì có trước sau thì phải giữ đúng trước sau.

    The third anomaly violates causality itself. If a sequence of causally related writes is read by someone else in reverse order, the conversation becomes nonsense. Consistent prefix reads is the guarantee that anyone reading such a sequence sees the writes in the order they occurred1. Kleppmann is explicit that this is a particular problem for partitioned databases: if different pieces of data live on different partitions and each partition replicates independently, there is no global write order to respect.

    Đọc thế nàoBa bảo đảm này càng lên càng khó, và bảo đảm thứ ba dẫn thẳng tới chủ đề nhân quả mà chương sau khai thác. Một lời giải là giữ mọi ghi có quan hệ nhân quả trên cùng một phân mảnh; nhưng khi không làm được, phải theo dõi nhân quả một cách tường minh.
    Ý chínhGiữ đúng thứ tự trước-sau của những việc có liên quan nhau.
    How to read itThe three guarantees get harder as you go, and the third leads straight to the theme of causality that the next chapters develop. One answer is to keep causally related writes on the same partition; when you can't, causality must be tracked explicitly.

    Vì sao ứng dụng không nên tự vá

    Kleppmann kết trục này bằng một khuyến nghị sắc: nếu ứng dụng phải tự dựng mọi cơ chế chống lại độ trễ nhân bản, mã nguồn sẽ phức tạp và rất dễ sai. Sẽ tốt hơn nhiều nếu ứng dụng chỉ cần tin vào cơ sở dữ liệu — và đó chính là lý do giao dịch tồn tại. Đây là cầu nối có chủ ý sang Chương 7.

    Ba, hay bốn?

    Bài báo gốc của Terry và cộng sự đề xuất bốn session guarantee: ngoài Read Your Writes và Monotonic Reads còn có Monotonic Writes và Writes Follow Reads8. Chương 5 trình bày ba và thay Writes Follow Reads bằng cách đóng khung mang tính nhân quả hơn là consistent prefix reads. Khi bạn thấy tài liệu khác đếm ra bốn hoặc năm bảo đảm, khác biệt nằm ở cách đóng khung của từng nguồn chứ không phải ở chỗ có nguồn nào sai.

  4. “Eventually” has no deadline

    Chữ “rốt cuộc” không đi kèm hạn chót“Eventually” comes with no deadline

    Điểm tinh tế nhất của trục này là ngôn ngữ. Eventual consistency là một lời hứa về giới hạn, không phải về thời gian: nó nói các bản sao sẽ hội tụ, nhưng không nói khi nào123. Một hệ mà độ trễ nhân bản bình thường là 10ms và một hệ mà độ trễ có thể vọt lên 10 phút khi tải nặng đều là “eventually consistent” như nhau. Vì vậy nhãn ấy hầu như không nói gì về trải nghiệm mà người dùng của bạn sẽ gặp — số đo mới nói.

    Có người hứa “kiểu gì rồi cũng chép xong”. Câu đó đúng, nhưng không cho biết là năm phút nữa hay ngày mai. Nhớ nhé: hứa “rồi cũng xong” thì chưa phải là hứa gì cả — phải hỏi “bao lâu?”.

    The subtlest point on this axis is linguistic. Eventual consistency is a promise about a limit, not about time: it says replicas will converge, not when123. A system whose replication lag is normally 10ms and one whose lag can reach 10 minutes under load are equally “eventually consistent”. So the label tells you almost nothing about what your users will experience — only measurement does.

    Dùng khi nàoĐưa độ trễ nhân bản vào bảng giám sát như một chỉ số hạng nhất, đo bằng percentile theo đúng tinh thần Chương 1. “Eventually consistent” là một nhãn kiến trúc; p99 của độ trễ nhân bản mới là con số bạn có thể cam kết.
    Hỏi gìChép chậm hơn bao lâu? Phải đo thì mới biết.
    When to usePut replication lag on the dashboard as a first-class metric, measured in percentiles in the spirit of Chapter 1. “Eventually consistent” is an architectural label; the p99 of replication lag is the number you can actually commit to.
Trục 3 · Multi-leader replicationAxis 3 · Multi-leader replication

Nhiều lãnh đạo: mua sẵn sàng bằng xung độtMulti-leader: buying availability with conflict

Mở rộng tự nhiên của mô hình trên là cho phép nhiều nút cùng nhận ghi, mỗi nút đồng thời đóng vai follower của các nút kia. Kleppmann nói thẳng rằng bên trong một trung tâm dữ liệu, cấu hình này hiếm khi bõ công vì độ phức tạp tăng vượt lợi ích1. Nó chỉ trở nên hợp lý ở ba hoàn cảnh: vận hành nhiều trung tâm dữ liệu, nơi mỗi trung tâm có leader riêng và độ trễ liên trung tâm được giấu khỏi đường ghi; client hoạt động ngoại tuyến, nơi mỗi thiết bị thực chất là một trung tâm dữ liệu tí hon có leader riêng; và soạn thảo cộng tác thời gian thực, nơi mỗi người biên tập giữ một bản sao cục bộ.

Bây giờ cho phép nhiều bạn cùng được viết vào sổ của mình, rồi thỉnh thoảng đưa nhau xem để ghép lại. Lợi: ai cũng viết được ngay, không phải chờ. Hại: có lúc hai bạn cùng sửa một dòng thành hai thứ khác nhau — và lúc ghép sổ phải quyết định nghe theo ai.

The natural extension is to let several nodes accept writes, each also acting as a follower of the others. Kleppmann says plainly that within a single data centre this configuration is rarely worth it, because the added complexity outweighs the benefit1. It becomes reasonable in three situations: multi-data-centre operation, where each centre has its own leader and inter-centre latency is hidden from the write path; clients with offline operation, where every device is effectively a tiny data centre with its own leader; and real-time collaborative editing, where each editor holds a local replica.

The biggest downside is one word: conflicts

Kleppmann gói cả trục này vào một câu: nhược điểm lớn của multi-leader là cùng một dữ liệu có thể bị sửa đồng thời ở hai trung tâm khác nhau, và những ghi xung đột đó phải được hóa giải1. Trong mô hình một-lãnh-đạo, xung đột không tồn tại vì leader áp đặt một thứ tự duy nhất. Bỏ leader duy nhất đi là bỏ luôn thứ tự duy nhất ấy — và bài toán xung đột không biến mất, nó chỉ chuyển từ tầng cơ sở dữ liệu lên tầng ứng dụng của bạn.

Nhiều người cùng được viết thì sẽ có lúc hai người viết hai câu khác nhau vào cùng một dòng. Lúc ghép sổ phải chọn: giữ câu nào, hay ghép cả hai lại? Nhớ nhé: cho nhiều người viết thì phải có cách xử lý lúc đụng nhau.

Kleppmann compresses the whole axis into one sentence: the big downside of multi-leader replication is that the same data may be concurrently modified in two different data centres, and those conflicting writes must be resolved1. In the single-leader model conflicts don't exist because the leader imposes one order. Removing the single leader removes that single order — and the conflict problem doesn't vanish, it just moves from the database up into your application.

Đáng chú ý là Kleppmann xếp ứng dụng lịch ngoại tuyến và Google Docs vào cùng một họ kiến trúc với nhân bản đa trung tâm dữ liệu. Nhìn bề ngoài chúng chẳng liên quan gì; nhìn theo cấu trúc thì giống hệt: mỗi thiết bị giữ một bản sao cục bộ, ghi được chấp nhận ngay tại chỗ mà không chờ ai, và việc đồng bộ diễn ra bất đồng bộ sau đó. Ông còn nhận xét rằng dưới góc nhìn đó, mỗi thiết bị là một “trung tâm dữ liệu”, và kết nối mạng giữa chúng cực kỳ không đáng tin. Chính cách đóng khung này làm cho một bài toán tưởng là chuyện của ứng dụng di động trở thành cùng một bài toán với nhân bản liên lục địa.

Ba hoàn cảnh Đa trung tâm dữ liệu, client ngoại tuyến, soạn thảo cộng tác. Trong một trung tâm thì hiếm khi bõ.
Cái giá là xung đột Mất thứ tự ghi duy nhất nghĩa là phải tự định nghĩa “đúng” khi hai bên bất đồng.
Topology quyết định nhân quả Vòng, sao, toàn-nối-toàn: mỗi cách có kiểu hỏng riêng về thứ tự và về điểm chết.
Khi nào dùng Khi ở xa nhau, khi mất mạng, hoặc khi nhiều người cùng sửa một tài liệu.
Sẽ có lúc đụng nhau Hai người sửa một dòng thành hai kiểu — phải có cách chọn.
Truyền tin kiểu nào Chuyền vòng tròn, qua một bạn trung tâm, hay ai cũng nói với ai.
Three situations Multi-data-centre, offline clients, collaborative editing. Within one data centre it rarely pays.
The price is conflict Losing the single write order means defining “correct” yourself when two sides disagree.
Topology shapes causality Circular, star, all-to-all: each fails differently on ordering and on single points of failure.
  1. Multi-datacenter operation

    Mỗi trung tâm dữ liệu một leaderOne leader per data centre

    Với một leader duy nhất, mọi ghi từ mọi nơi trên thế giới phải đi qua đúng một trung tâm dữ liệu. Đặt một leader ở mỗi trung tâm đổi bức tranh đó: ghi được xử lý cục bộ rồi nhân bản bất đồng bộ sang các trung tâm khác. Kleppmann so sánh trên ba mặt1 — hiệu năng: độ trễ liên trung tâm bị giấu khỏi người dùng; chịu lỗi trung tâm: mỗi trung tâm tiếp tục chạy độc lập khi trung tâm khác sập; chịu lỗi mạng: vì lưu lượng giữa các trung tâm vốn bất đồng bộ, một sự cố mạng tạm thời không chặn được đường ghi.

    Nếu cả trường chỉ có một cuốn sổ đặt ở phòng hiệu trưởng, thì ai muốn ghi cũng phải chạy lên đó. Thay vào đó, mỗi lớp giữ một cuốn sổ riêng và cuối ngày các lớp trao đổi cho khớp. Ghi nhanh hơn hẳn, và lớp này nghỉ thì lớp kia vẫn ghi được. Nhớ nhé: để sổ ở gần thì viết nhanh hơn.

    With a single leader, every write from anywhere on Earth must pass through one data centre. Putting a leader in each centre changes the picture: writes are processed locally and replicated asynchronously to the others. Kleppmann compares on three fronts1 — performance: inter-centre latency is hidden from users; tolerance of centre outages: each centre keeps running independently when another fails; tolerance of network problems: since inter-centre traffic is asynchronous anyway, a temporary network fault doesn't block the write path.

    Đọc thế nàoKleppmann kèm một cảnh báo đáng nhớ: multi-leader thường bị xếp thêm vào sau như một tính năng bổ sung, nên hay va chạm với những phần khác của cơ sở dữ liệu — khóa tự tăng, trigger, ràng buộc toàn vẹn. Ông gọi đây là vùng nguy hiểm, nên nếu dùng thì phải dùng rất cẩn thận.
    Cẩn thậnCách này tiện nhưng dễ sinh rắc rối ở những chỗ không ngờ.
    How to read itKleppmann adds a memorable warning: multi-leader is often retrofitted as an add-on feature, so it collides with other parts of the database — autoincrementing keys, triggers, integrity constraints. He calls it dangerous territory, to be used with great care if at all.
  2. Clients with offline operation

    Mỗi thiết bị là một trung tâm dữ liệuEvery device is a data centre

    Ứng dụng lịch trên điện thoại phải cho bạn thêm sự kiện ngay cả khi máy bay đang bay và không có mạng. Nghĩa là mỗi thiết bị giữ một cơ sở dữ liệu cục bộ đóng vai leader — nó nhận ghi ngay — và khi có mạng trở lại, các bản sao trên nhiều thiết bị đồng bộ với nhau. Kleppmann mô tả đây đúng là nhân bản nhiều lãnh đạo, với đặc thù là độ trễ nhân bản có thể lên tới hàng giờ hoặc hàng ngày1. CouchDB được ông nêu như hệ thống thiết kế thẳng cho kiểu hoạt động này.

    Bạn ghi lịch học vào sổ tay khi đang ở chỗ không có mạng. Điện thoại vẫn cho bạn ghi — nó ghi vào “cuốn sổ riêng” của nó. Khi có mạng, nó mới đem so với các cuốn khác. Nhớ nhé: ghi được ngay là vì mỗi máy giữ một cuốn sổ của riêng mình.

    A calendar app on a phone must let you add an event mid-flight with no network. That means each device keeps a local database acting as a leader — it accepts writes immediately — and when connectivity returns, the replicas on your various devices sync with one another. Kleppmann describes this as exactly multi-leader replication, distinguished by a replication lag that may run to hours or days1. He names CouchDB as a system designed squarely for this mode of operation.

    Dùng khi nàoMọi ứng dụng phải dùng được khi mất mạng đều đã ngầm chọn multi-leader, dù đội phát triển có gọi tên nó hay không. Nếu bạn từng viết hàng đợi đồng bộ “tự chế” trong một app di động, bạn đã tự dựng một phiên bản nghiệp dư của mô hình này — kèm theo bài toán xung đột đi cùng.
    Ý chínhApp dùng được lúc mất mạng thì máy nào cũng phải có sổ riêng.
    When to useAny application that must work offline has already chosen multi-leader, whether or not the team ever named it. If you've written a homemade sync queue in a mobile app, you built an amateur version of this model — conflict problem included.

    CouchDB làm điều đó ra sao

    Tài liệu CouchDB dựng đúng ví dụ minh họa: Alice đồng bộ danh thiếp của Bob giữa máy bàn và laptop, sửa e-mail ở một máy và số điện thoại ở máy kia, rồi nhân bản hai chiều. Kết quả là cả hai phiên bản cùng tồn tại ở cả hai phía; CouchDB chọn một phiên bản “thắng” bằng thuật toán tất định để mọi bên chọn giống nhau, còn phiên bản kia được giữ lại như một nhánh xung đột trong cây phiên bản16. Nói cách khác, cơ sở dữ liệu không vứt dữ liệu — nó giấu dữ liệu, và ứng dụng phải chủ động trình cả hai phiên bản cho người dùng ghép lại, nếu không thì thay đổi coi như mất.

    Trên một nút thì lại khác

    Vẫn theo tài liệu CouchDB, khi làm việc trên một nút duy nhất thì hệ tránh xung đột bằng cách yêu cầu client gửi kèm _rev của phiên bản trước; nếu bản đó đã bị thay thế, cập nhật bị từ chối với mã 409 Conflict16. Đây là minh họa sạch cho luận điểm của chương: xung đột không phải do dữ liệu nhiều bản sinh ra, mà do đồng thời cộng với không có thứ tự chung; có một nút thì vẫn có đồng thời, nhưng có thứ tự chung để mà từ chối.

  3. Real-time collaborative editing

    Soạn thảo cộng tác: khóa càng nhỏ càng tốtCollaborative editing: make the lock tiny

    Google Docs cho nhiều người sửa cùng một tài liệu cùng lúc. Kleppmann chỉ ra đây cũng là bài toán nhân bản nhiều lãnh đạo: thay đổi được áp ngay lên bản sao cục bộ của từng người rồi mới đồng bộ bất đồng bộ với máy chủ và với người khác1. Muốn tránh xung đột thì phải khóa tài liệu trước khi sửa — nhưng khóa cả tài liệu thì hết cộng tác. Lời giải là thu đơn vị thay đổi xuống thật nhỏ, chẳng hạn từng lần gõ phím, và chấp nhận phải hóa giải xung đột. Chính đội Google Docs mô tả bước chuyển này khi họ viết lại sản phẩm năm 2010: chuyển từ đồng bộ theo tài liệu sang mô hình cộng tác ở mức từng ký tự22.

    Nếu mỗi lần một bạn sửa bài là cả tờ giấy bị khóa lại, thì các bạn khác chỉ ngồi chờ — thế thì làm chung làm gì. Cách hay hơn: ai sửa chữ nào thì chỉ “giữ” đúng chữ đó, phần còn lại vẫn để mọi người viết. Nhớ nhé: chia nhỏ ra thì nhiều người mới cùng làm được.

    Google Docs lets many people edit one document at once. Kleppmann points out this too is a multi-leader replication problem: changes are applied instantly to each person's local replica and only then synced asynchronously with the server and the others1. To avoid conflicts you would lock the document before editing — but locking the whole document ends collaboration. The answer is to shrink the unit of change until it is tiny, down to a single keystroke, and accept that conflicts must be resolved. The Google Docs team described exactly this shift when they rewrote the product in 2010: from document-level syncing to character-level collaboration22.

    Hệ quả thực tếKích thước của đơn vị nhân bản là một tham số thiết kế, không phải một hằng số. Khóa to thì ít xung đột nhưng ít cộng tác; khóa nhỏ thì cộng tác mượt nhưng phải đầu tư vào cơ chế hóa giải. Chọn sai chỗ này là lý do nhiều tính năng “cùng chỉnh sửa” trong sản phẩm nội bộ chỉ chạy được trên giấy.
    Ý chínhKhóa cả trang thì không ai làm chung được; khóa từng chữ thì mới làm chung được.
    The practical upshotThe size of the replication unit is a design parameter, not a constant. Big locks mean few conflicts and little collaboration; small ones mean smooth collaboration and real investment in resolution. Getting this wrong is why many in-house “co-editing” features only work on paper.

    Hai dòng kỹ thuật

    Hai họ thuật toán cạnh tranh nhau ở đây. Operational transformation biến đổi các thao tác đồng thời sao cho áp theo thứ tự nào cũng ra cùng kết quả — đây là đường mà Google Docs và Etherpad đi3. CRDT (conflict-free replicated data types) thì thiết kế chính cấu trúc dữ liệu sao cho hợp nhất luôn hội tụ, không cần điều phối; công trình nền của Shapiro và cộng sự tại INRIA hệ thống hóa họ kiểu dữ liệu này20. Chính Kleppmann cùng Alastair Beresford sau đó đề xuất một kiểu dữ liệu JSON không-xung-đột, đưa CRDT lên cấu trúc lồng nhau tùy ý thay vì chỉ các kiểu đơn giản19.

    Vì sao phần này đáng đọc lại sau 2017

    Mục CRDT trong chương được viết khi họ kỹ thuật này còn non. Từ đó tới nay nó đã đi vào sản phẩm thật (thư viện Automerge chính là hậu duệ trực tiếp của bài báo JSON CRDT nói trên19). Khi đọc đoạn Kleppmann gọi CRDT là “một lĩnh vực nghiên cứu đang lên”, hãy đọc nó như một ảnh chụp thời điểm 2017 chứ không phải một đánh giá về hiện trạng.

  4. Handling write conflicts

    Hóa giải xung đột: tránh, hội tụ, hoặc tự viết luậtResolving conflicts: avoid, converge, or write the rule

    Kleppmann xếp các lời giải thành ba tầng1. Tránh xung đột là chiến lược được khuyến nghị nhất và cũng đơn giản nhất: định tuyến mọi ghi cho một bản ghi cụ thể luôn về cùng một leader — nhưng nó vỡ ngay khi leader đó phải đổi, ví dụ khi một trung tâm dữ liệu hỏng. Hội tụ về một trạng thái nhất quán là yêu cầu bắt buộc khi xung đột đã xảy ra: mọi bản sao phải kết thúc ở cùng một giá trị. Các cách phổ biến là gán mỗi ghi một ID duy nhất rồi giữ ghi có ID lớn nhất — tức last write wins — hoặc ưu tiên theo bản sao, hoặc ghép các giá trị lại, hoặc ghi lại toàn bộ thông tin xung đột rồi để người dùng quyết định sau. Logic riêng là tầng thứ ba: nhiều hệ cho phép chạy mã hóa giải của bạn lúc ghi hoặc lúc đọc.

    Khi hai bạn sửa cùng một dòng, có ba cách xử lý. Một: sắp xếp sao cho mỗi dòng chỉ do một bạn phụ trách, khỏi đụng nhau. Hai: đặt ra một luật máy móc để luôn chọn được một câu, ví dụ “ai viết sau thì thắng”. Ba: giữ lại cả hai câu và hỏi người thật xem chọn cái nào. Nhớ nhé: luật máy móc thì nhanh nhưng có thể vứt mất bài của một bạn.

    Kleppmann sorts the answers into three tiers1. Conflict avoidance is the most recommended and the simplest: route all writes for a given record to the same leader — but it breaks the moment that leader must change, for instance when a data centre fails. Converging toward a consistent state is mandatory once a conflict has occurred: every replica must end up at the same value. Common ways are to give each write a unique ID and keep the highest — last write wins — or to prefer one replica, or to merge the values, or to record the conflict explicitly and let the user decide later. Custom logic is the third tier: many systems let you run your own resolution code on write or on read.

    Cảnh báoKleppmann nói rõ rằng last write wins dễ gây mất dữ liệu, dù nó phổ biến. Hãy đọc mọi cấu hình “LWW” như một quyết định vứt bỏ dữ liệu có chủ ý, không phải như một cơ chế sửa lỗi. Chi tiết ở trục 4.
    Cẩn thận“Ai viết sau thì thắng” nghĩa là bài của người viết trước bị xóa hẳn.
    A warningKleppmann states plainly that last write wins is prone to data loss, popular though it is. Read any “LWW” setting as a deliberate decision to throw data away, not as an error-correction mechanism. Details on axis 4.

    Xung đột không nhất thiết là một dòng

    Ông cũng lưu ý rằng phần lớn cơ chế hóa giải xung đột hoạt động ở mức một hàng hoặc một tài liệu, chứ không ở mức giao dịch. Nếu một giao dịch của bạn sửa nhiều bản ghi, mỗi ghi có thể được hóa giải độc lập với nhau, và kết quả cuối cùng có thể không tương ứng với bất kỳ giao dịch nào đã từng được gửi lên. Đây là một trong những chỗ dễ khiến người mới đánh giá thấp rủi ro của multi-leader.

    Lúc ghi hay lúc đọc

    Hóa giải lúc ghi chạy ngay khi hệ phát hiện bất đồng trong log nhân bản, thường trong một tiến trình nền không có ngữ cảnh người dùng. Hóa giải lúc đọc thì trả về tất cả phiên bản xung đột và để ứng dụng — hoặc con người — quyết định, như cách CouchDB giữ nhánh xung đột trong cây phiên bản16. Lựa chọn ở đây là lựa chọn về ai có đủ thông tin để phán: mã nền, ứng dụng, hay người dùng.

  5. Multi-leader replication topologies

    Topology: vòng, sao, hay toàn-nối-toànTopologies: circular, star, or all-to-all

    Khi có nhiều hơn hai leader, còn phải chọn đường đi của các ghi. Kleppmann nêu ba topology1: vòng tròn, mỗi nút chuyển ghi cho nút kế tiếp; hình sao, một nút gốc chuyển tiếp cho tất cả; và toàn-nối-toàn, mỗi leader gửi thẳng cho mọi leader khác. Vòng và sao có chung điểm yếu: một nút hỏng là dòng nhân bản đứt. Toàn-nối-toàn tránh được điều đó nhưng đẻ ra một vấn đề khác — các đường mạng có tốc độ khác nhau, nên một ghi có thể tới đích trước cái ghi mà nó phụ thuộc nhân quả.

    Truyền tin trong lớp có ba kiểu. Chuyền dây: bạn này nói cho bạn kế bên, cứ thế vòng quanh — một bạn nghỉ là đứt. Qua lớp trưởng: ai cũng nói với lớp trưởng, lớp trưởng báo lại cả lớp — lớp trưởng nghỉ là im hết. Ai cũng nói với ai: chắc chắn hơn, nhưng tin có thể tới lộn xộn, câu sau nghe được trước câu trước. Nhớ nhé: mỗi cách truyền tin hỏng theo một kiểu riêng.

    With more than two leaders you must also choose the route writes take. Kleppmann names three topologies1: circular, each node forwarding to the next; star, one root node forwarding to all; and all-to-all, every leader sending directly to every other. Circular and star share a weakness: one failed node breaks the replication flow. All-to-all avoids that but introduces another problem — network links have different speeds, so a write can arrive before the write it causally depends on.

    Đọc thế nàoTopology không phải chi tiết vận hành mà là một quyết định về nhân quả. Nếu bạn chọn toàn-nối-toàn, bạn đã nhận lấy trách nhiệm theo dõi thứ tự nhân quả một cách tường minh — và Kleppmann lưu ý rằng dấu thời gian vật lý không làm nổi việc đó vì đồng hồ giữa các máy không đủ tin cậy.
    Ý chínhCách truyền tin quyết định chuyện tin có tới đúng thứ tự hay không.
    How to read itTopology is not an operational detail but a decision about causality. Choosing all-to-all means taking on the job of tracking causal order explicitly — and Kleppmann notes that physical timestamps can't do it, because clocks across machines aren't trustworthy enough.

    Vòng lặp vô hạn là chuyện có thật

    Trong topology vòng và sao, mỗi ghi mang theo danh sách các nút nó đã đi qua để không bị chuyền mãi không dứt. Chuyện xảy ra khi cơ chế này sai được ghi lại trong chính hệ thống theo dõi lỗi của Apache HBase: báo cáo HBASE-7709 mô tả khả năng xuất hiện vòng lặp vô hạn trong nhân bản master/master24. Kleppmann dẫn đúng báo cáo này trong danh mục tham chiếu của chương3.

    Vì sao dấu thời gian không cứu được

    Cách vá thường thấy cho chuyện nhân quả bị đảo là gắn dấu thời gian vào mỗi ghi rồi sắp lại theo đó. Nhưng đồng hồ giữa các máy lệch nhau, và độ lệch ấy có thể lớn hơn khoảng cách thời gian giữa hai ghi có quan hệ nhân quả — nên cách vá này không đáng tin. Đây chính là cây cầu dẫn tới khái niệm happens-before và version vector ở trục 4: cần một cách sắp thứ tự dựa trên quan hệ nhân quả quan sát được, không dựa vào đồng hồ.

Trục 4 · Leaderless replicationAxis 4 · Leaderless replication

Không lãnh đạo: quorum, và chỗ quorum không giữ lờiLeaderless: quorums, and where they break their promise

Họ thứ ba bỏ hẳn vai leader: client gửi mỗi lệnh ghi tới nhiều bản sao song song, và gửi mỗi lệnh đọc tới nhiều bản sao song song, rồi so các phiên bản nhận về1. Kleppmann gọi họ này là kiểu Dynamo, theo hệ kho khóa-giá trị của Amazon mà Riak, Cassandra và Voldemort lấy làm hình mẫu. Ý tưởng gốc còn cũ hơn nhiều: từ năm 1979, David Gifford đã trình bày cơ chế bỏ phiếu có trọng số cho dữ liệu nhân bản, trong đó mỗi thao tác đọc và ghi phải thu đủ số phiếu, và giao nhau giữa hai tập phiếu là thứ bảo đảm tính đúng đắn6. Chỗ khác biệt của Dynamo là biến các tham số ấy thành núm vặn cho người vận hành, đổi lấy tính sẵn sàng gần như tuyệt đối cho đường ghi.

Bây giờ không ai làm chủ cuốn sổ nữa. Bạn muốn ghi thì đi ghi vào ba cuốn cùng lúc; muốn đọc thì cũng đi hỏi ba cuốn cùng lúc rồi xem cuốn nào ghi mới nhất. Miễn là số cuốn bạn ghi cộng với số cuốn bạn hỏi nhiều hơn tổng số cuốn, thì kiểu gì cũng có ít nhất một cuốn vừa được ghi vừa được hỏi.

The third family abandons the leader role entirely: the client sends every write to several replicas in parallel, sends every read to several replicas in parallel, and compares the versions it gets back1. Kleppmann calls this the Dynamo style, after Amazon's key-value store on which Riak, Cassandra and Voldemort are modelled. The underlying idea is far older: in 1979 David Gifford described weighted voting for replicated data, in which every read and write must collect enough votes and the intersection of the two vote sets is what guarantees correctness6. Dynamo's contribution was to turn those parameters into operator-facing dials, in exchange for near-total availability on the write path.

w + r > n — a condition, not a guarantee

Với n bản sao, nếu mỗi ghi được xác nhận bởi ít nhất w nút và mỗi đọc hỏi ít nhất r nút, thì điều kiện w + r > n buộc hai tập nút phải giao nhau ở ít nhất một nút — nên lệnh đọc thường chạm được bản mới nhất1. Bài báo Dynamo viết cùng bất đẳng thức ấy dưới dạng R + W > N và nói rõ nó “cho ra một hệ kiểu quorum”5. Chữ quan trọng nhất trong cả đoạn này là chữ “thường”: Kleppmann dành hẳn một mục để liệt kê những trường hợp biên mà điều kiện vẫn đúng còn tính nhất quán thì không.

Lớp có 3 cuốn sổ. Bạn ghi vào 2 cuốn, rồi hỏi 2 cuốn. Vì 2 + 2 lớn hơn 3, kiểu gì cũng có ít nhất một cuốn nằm trong cả hai lần — nên bạn sẽ gặp lại chữ mình vừa ghi. Nhớ nhé: ghi nhiều cộng hỏi nhiều mà lớn hơn tổng số cuốn thì mới chắc gặp nhau.

With n replicas, if every write is confirmed by at least w nodes and every read queries at least r nodes, the condition w + r > n forces the two node sets to overlap in at least one node — so a read usually reaches the newest value1. The Dynamo paper writes the same inequality as R + W > N and says it “yields a quorum-like system”5. The most important word in the whole passage is “usually”: Kleppmann devotes a section to listing the edge cases where the condition still holds but consistency does not.

Ký hiệu khác nhau giữa các nguồn, nội dung thì như nhau. Sách dùng chữ thường w, r, n; bài Dynamo dùng chữ hoa R, W, N5; tài liệu Cassandra viết W + R > RF, trong đó RF là hệ số nhân bản, và mô tả các mức nhất quán của mình đúng là “một phiên bản của cơ chế R + W > N của Dynamo”13. Con số cụ thể đáng nhớ: cấu hình (N, R, W) phổ biến ở nhiều thực thể Dynamo trong Amazon là (3, 2, 2), và các phép đo trong bài báo được lấy trên hệ thật chạy đúng cấu hình đó5. Khi thấy tài liệu nào viết công thức bằng ký hiệu khác, hãy kiểm tra ký hiệu chứ đừng vội cho là nguồn ấy sai.

w + r > n Điều kiện giao nhau giữa tập ghi và tập đọc. Là điều kiện cần, không phải bảo đảm tuyệt đối.
Sloppy quorum Ghi vào nút “tạm trú” khi nút đúng không tới được — tăng sẵn sàng, phá tính giao nhau.
LWW vứt dữ liệu Hóa giải bằng dấu thời gian đạt hội tụ bằng cách xóa hẳn ghi của bên thua.
Ghi nhiều, hỏi nhiều Ghi vào mấy cuốn cộng hỏi mấy cuốn phải lớn hơn tổng số cuốn.
Ghi tạm chỗ khác Cuốn đúng không có thì ghi tạm cuốn khác — tiện, nhưng lúc hỏi có thể không gặp.
“Ai sau thì thắng” Cách này gọn, nhưng bài của người viết trước bị xóa mất thật.
w + r > n The overlap condition between write set and read set. A necessary condition, not an absolute guarantee.
Sloppy quorum Writing to stand-in nodes when the right ones are unreachable — more availability, no overlap.
LWW discards data Timestamp resolution reaches convergence by permanently deleting the losing write.
  1. Read repair & anti-entropy

    Hai cơ chế giữ các bản sao hội tụTwo mechanisms that keep replicas converging

    Không có leader nghĩa là không có ai chủ động đẩy thay đổi tới bản sao bị lỡ. Kleppmann nêu hai cơ chế bù lại1. Read repair: khi client đọc song song nhiều bản sao và phát hiện một bản mang giá trị cũ, nó ghi đè giá trị mới xuống bản đó — hiệu quả với dữ liệu hay được đọc. Anti-entropy: một tiến trình nền liên tục dò tìm khác biệt giữa các bản sao và chép phần thiếu. Cơ chế thứ hai quan trọng vì có những giá trị hiếm khi ai đọc; nếu chỉ trông vào read repair thì chúng có thể nằm lệch rất lâu, làm giảm độ bền vững.

    Không có cô giáo đọc cho cả lớp chép, nên phải có hai cách khác để các cuốn sổ khớp nhau. Một: mỗi lần ai đó mở nhiều cuốn ra so, thấy cuốn nào thiếu thì chép bù ngay. Hai: có một bạn rảnh đi so từng cuốn với nhau đều đặn. Nhớ nhé: cuốn nào chẳng ai mở ra xem thì phải có người đi kiểm tra định kỳ.

    No leader means nobody actively pushes changes to a replica that missed them. Kleppmann names two compensating mechanisms1. Read repair: when a client reads several replicas in parallel and spots a stale one, it writes the newer value back to it — effective for frequently-read data. Anti-entropy: a background process constantly looks for differences between replicas and copies what's missing. The second matters because some values are rarely read; relying on read repair alone would leave them divergent for a long time, reducing durability.

    Trong sản phẩm thậtTài liệu Cassandra mô tả đúng cặp cơ chế này — read repair ở đường đọc, hinted handoff ở đường ghi — và nói rõ cả hai chỉ là best-effort; để bảo đảm nhất quán rốt cuộc, hệ dựa vào anti-entropy repair dùng cây Merkle để so dữ liệu giữa các bản sao13. Bài báo Dynamo cũng dùng cây Merkle cho đúng mục đích đó5.
    Ý chínhVừa chép bù lúc mở sổ ra xem, vừa có người đi kiểm tra định kỳ.
    In real productsCassandra's documentation describes exactly this pair — read repair on the read path, hinted handoff on the write path — and states that both are only best-effort; to guarantee eventual consistency the system relies on anti-entropy repair using Merkle trees to compare data across replicas13. The Dynamo paper uses Merkle trees for the same purpose5.
  2. Limitations of quorum consistency

    Năm chỗ công thức vẫn đúng mà dữ liệu vẫn saiFive places the formula holds and the data still lies

    Đây là mục quan trọng nhất của cả trục, và cũng là mục hay bị bỏ qua nhất. Kleppmann liệt kê những tình huống mà w + r > n vẫn được thỏa mà đọc vẫn không thấy giá trị mới nhất1: khi dùng sloppy quorum, tập nút ghi và tập nút đọc có thể không giao nhau; khi hai lệnh ghi xảy ra đồng thời, không có cách nào xác định cái nào “mới hơn” nếu chỉ dựa vào đồng hồ; khi một lệnh ghi đồng thời với một lệnh đọc, ghi có thể mới chỉ tới một phần các bản sao; khi một lệnh ghi thất bại một phần — thành công trên ít hơn w nút và không được hoàn tác trên những nút đã nhận; và khi một nút mang dữ liệu mới chết rồi được khôi phục từ bản sao cũ, làm số bản sao mang giá trị mới tụt xuống dưới w.

    Công thức “ghi mấy cuốn + hỏi mấy cuốn > tổng số cuốn” nghe rất chắc, nhưng có mấy lúc nó vẫn hụt: khi bạn ghi tạm vào cuốn khác vì cuốn đúng không có ở đó; khi hai người ghi cùng lúc nên không biết ai trước ai sau; khi ghi mới được nửa chừng; và khi một cuốn bị hỏng rồi người ta chép lại từ bản cũ. Nhớ nhé: công thức đúng không có nghĩa là lúc nào cũng đúng.

    This is the most important part of the axis and the most often skipped. Kleppmann lists situations where w + r > n is satisfied and a read still misses the newest value1: with a sloppy quorum, the write set and the read set may not overlap at all; when two writes are concurrent, there is no way to say which is “newer” from clocks alone; when a write is concurrent with a read, the write may have reached only some replicas; when a write partially fails — succeeding on fewer than w nodes and not being rolled back on those that did take it; and when a node carrying new data dies and is restored from an old replica, dropping the count of up-to-date replicas below w.

    Cách đọc đúngHãy đọc w + r > n như một điều kiện cần để tập ghi và tập đọc giao nhau, chứ không phải một bảo đảm rằng bạn luôn đọc thấy ghi mới nhất. Kleppmann nói rõ rằng quorum trong Dynamo không cho các bảo đảm mạnh mà giao dịch ACID cung cấp, và rằng ngay cả read-your-writes hay monotonic reads cũng không được bảo đảm sẵn ở đây.
    Cẩn thậnCông thức đúng trên giấy vẫn có lúc hụt trong thực tế.
    How to read it properlyRead w + r > n as a necessary condition for the write and read sets to overlap, not as a guarantee that you always see the newest write. Kleppmann is explicit that Dynamo-style quorums do not give the strong guarantees ACID transactions provide, and that even read-your-writes or monotonic reads are not guaranteed here.

    Giám sát thay cho niềm tin

    Vì không có bảo đảm cứng, Kleppmann nhấn mạnh việc đo độ cũ của dữ liệu. Với hệ leader-based, độ trễ nhân bản đo được trực tiếp vì có một thứ tự ghi duy nhất để so. Với hệ leaderless, không có thứ tự ghi cố định nên việc đo khó hơn nhiều — và nếu hệ chỉ dùng read repair mà không có anti-entropy, một giá trị hiếm khi được đọc có thể nằm lệch vô hạn định, khiến không thể nói bất cứ điều gì chắc chắn về độ cũ.

    Có thể nói xác suất

    Ngay cả khi không có bảo đảm tuyệt đối, vẫn đo được khả năng đọc thấy dữ liệu mới. Kleppmann dẫn công trình “Quantifying Eventual Consistency with PBS” của Peter Bailis và cộng sự — một khung phân tích tính tươi của dữ liệu trong hệ quorum theo xác suất thay vì theo mệnh đề tuyệt đối3. Đây là thái độ đúng với loại hệ này: thay câu hỏi “có bảo đảm không” bằng câu hỏi “xác suất bao nhiêu, và bạn có đo được nó không”.

  3. Sloppy quorums & hinted handoff

    Quorum “lỏng”: sẵn sàng đổi bằng lời hứaSloppy quorums: availability bought with the promise

    Khi một phân vùng mạng cắt client khỏi đúng những nút “nhà” của một khóa, hệ có hai lựa chọn: trả về lỗi, hoặc chấp nhận ghi vào bất kỳ w nút nào tới được — dù không phải nút phụ trách khóa đó. Lựa chọn thứ hai là sloppy quorum; khi phân vùng lành lại, các ghi tạm trú được chuyển về đúng nhà của chúng, cơ chế gọi là hinted handoff1. Bài báo Dynamo mô tả chính xác cơ chế này: vì quorum chặt sẽ làm hệ mất khả dụng khi có sự cố, Dynamo không áp đặt thành viên quorum chặt mà dùng “sloppy quorum” trên n nút khỏe mạnh đầu tiên trong danh sách ưu tiên; bản sao được gửi nhờ mang theo một hint ghi rõ nút nhận đúng là ai, và được chuyển trả khi nút đó hồi phục5.

    Bạn muốn ghi vào ba cuốn sổ của tổ 1, nhưng tổ 1 đi vắng hết. Bạn ghi tạm vào sổ của tổ 2, kèm một mẩu giấy: “cái này là của tổ 1 nhé”. Khi tổ 1 về, tổ 2 đưa lại. Lợi: bạn không phải chờ. Hại: trong lúc đó ai đi hỏi sổ tổ 1 sẽ không thấy chữ bạn vừa ghi. Nhớ nhé: ghi tạm chỗ khác thì người đi hỏi đúng chỗ vẫn không tìm ra.

    When a network partition cuts a client off from precisely the “home” nodes for a key, the system has two options: return an error, or accept the write on any w reachable nodes, even ones not responsible for that key. The second is a sloppy quorum; once the partition heals, the temporarily-hosted writes are forwarded to their proper homes, a mechanism called hinted handoff1. The Dynamo paper describes exactly this: because a traditional quorum would be unavailable during failures, Dynamo does not enforce strict quorum membership but uses a “sloppy quorum” over the first n healthy nodes in the preference list; the stand-in replica carries a hint naming the intended recipient and is delivered back when that node recovers5.

    Đọc thế nàoKleppmann nói thẳng: sloppy quorum không phải là quorum theo nghĩa truyền thống mà chỉ là một cam kết về độ bền vững. Nó rất hữu ích để tăng khả năng chấp nhận ghi, nhưng nó có nghĩa là ngay cả khi w + r > n, bạn không thể chắc lệnh đọc sẽ chạm bản mới nhất — vì bản mới nhất có thể đang nằm nhờ ở một nút khác.
    Cẩn thậnGhi tạm ở chỗ khác thì hỏi đúng chỗ vẫn có thể không thấy.
    How to read itKleppmann says it flatly: a sloppy quorum is not a quorum in the traditional sense, only a durability assurance. It is very useful for raising write acceptance, but it means that even with w + r > n you cannot be sure a read reaches the newest value — that value may be lodging on some other node.

    Núm vặn nào cho việc gì

    Bài báo Dynamo chỉ rõ các tham số này mua những thứ khác nhau: đặt W = 1 cho mức sẵn sàng cao nhất — ghi được chấp nhận miễn là một nút đã lưu bền được khóa xuống kho cục bộ — nhưng phần lớn dịch vụ trong sản xuất ở Amazon vẫn đặt W cao hơn để đạt mức bền vững mong muốn5. Đọc ngược lại: mỗi lần bạn hạ w để hệ “sẵn sàng hơn”, bạn đang rút ngắn cửa sổ an toàn của độ bền vững.

    Vì sao Dynamo chọn như vậy

    Lựa chọn này bắt nguồn từ một yêu cầu nghiệp vụ rất cụ thể: giỏ hàng của Amazon phải cho khách thêm và bớt món ngay cả khi mạng và máy chủ đang có sự cố, nên bài báo nói rõ rằng yêu cầu đó buộc họ đẩy độ phức tạp của việc hóa giải xung đột sang phía đọc để bảo đảm ghi không bao giờ bị từ chối5. Toàn bộ họ kiến trúc leaderless là hệ quả kỹ thuật của một quyết định sản phẩm.

  4. Last write wins (LWW)

    LWW: hội tụ bằng cách vứt bỏ dữ liệuLWW: converging by throwing data away

    Một cách đạt hội tụ là tuyên bố mỗi bản sao chỉ giữ giá trị “mới nhất” và cho phép ghi đè các giá trị “cũ hơn”. Kleppmann chỉ ra chỗ hỏng ngay trong tiền đề: khi các lệnh ghi đồng thời, thứ tự giữa chúng không xác định, nên “mới nhất” là một khái niệm được áp đặt một cách tùy tiện — thường bằng dấu thời gian1. Cơ chế này đạt hội tụ, nhưng phải trả bằng mất bền vững: nếu nhiều ghi đồng thời cùng báo thành công cho client, chỉ một cái sống sót, những cái còn lại bị lặng lẽ vứt bỏ. LWW là lựa chọn đánh đổi dữ liệu lấy sự đơn giản, không phải một cơ chế sửa lỗi.

    Hai bạn cùng viết vào một dòng. Luật “ai viết sau thì thắng” sẽ chọn một bạn — và xóa hẳn bài của bạn kia. Cả hai đều được khen là “đã nộp bài”, nhưng chỉ một bài còn tồn tại. Nhớ nhé: luật này gọn thật, nhưng nó làm mất bài của người ta.

    One way to converge is to declare that each replica keeps only the “most recent” value and lets it overwrite “older” ones. Kleppmann finds the flaw in the premise: when writes are concurrent, the order between them is undefined, so “most recent” is a notion imposed arbitrarily — usually by timestamp1. The mechanism achieves convergence, but at the cost of durability: if several concurrent writes all reported success to their clients, only one survives and the rest are silently discarded. LWW is a choice to trade data for simplicity, not an error-correction mechanism.

    Dùng khi nàoKleppmann nêu một trường hợp LWW an toàn: khi khóa chỉ được ghi đúng một lần rồi bất biến sau đó — chẳng hạn dùng UUID làm khóa cho mỗi thao tác ghi, khiến không bao giờ có hai ghi cùng khóa. Ngoài tình huống đó, bật LWW là chấp nhận mất dữ liệu.
    Khi nào được dùngChỉ an toàn khi mỗi chỗ chỉ được viết đúng một lần rồi thôi.
    When to useKleppmann names one safe case for LWW: when a key is written exactly once and thereafter immutable — for instance using a UUID as the key for each write, so two writes never share a key. Outside that, enabling LWW is accepting data loss.

    Khung của sách, 2017

    Trong chương, Cassandra được nêu là hệ chỉ cung cấp LWW, còn Riak cho phép bật cấu trúc dữ liệu giữ mọi phiên bản xung đột thay vì vứt bớt. Đây là ảnh chụp ở thời điểm sách viết và nên được đọc như vậy.

    Tài liệu hiện thời nói gì

    Tài liệu Cassandra hiện nay xác nhận cơ chế vẫn là vậy: hệ dùng dấu thời gian của mutation — từ đồng hồ client hoặc đồng hồ nút điều phối — và “các cập nhật được hóa giải theo quy tắc last write wins”, kèm lưu ý rằng tính đúng đắn của Cassandra phụ thuộc vào những đồng hồ này, nên phải chạy đồng bộ thời gian kiểu NTP13. Về phía Riak, tài liệu mô tả hai núm vặn: nếu allow_mult đặt false, Riak tự hóa giải và không trả sibling về cho client; nếu last_write_wins đặt false, hệ hóa giải theo dấu thời gian — và tài liệu nói thẳng rằng dấu thời gian không phải cơ chế hóa giải đáng tin trong hệ phân tán15. Chính tài liệu Riak khuyến nghị luôn đặt allow_mult là true14.

  5. Happens-before & version vectors

    “Đồng thời” là một khái niệm nhân quả, không phải đồng hồ“Concurrent” is a causal notion, not a clock one

    Để phát hiện ghi đồng thời, cần một định nghĩa không dựa vào thời gian vật lý. Kleppmann dùng quan hệ happens-before: thao tác B xảy ra sau A nếu B biết về A, phụ thuộc vào A, hoặc xây trên A; nếu không thao tác nào biết về thao tác kia thì chúng đồng thời1. Định nghĩa này đến thẳng từ bài báo năm 1978 của Leslie Lamport, nơi ông xây một thứ tự bộ phận cho các sự kiện trong hệ phân tán mà không cần đồng hồ vật lý7. Điểm mấu chốt: đồng thời ở đây không có nghĩa “xảy ra cùng một khoảnh khắc”, mà nghĩa không cái nào biết về cái nào — hai ghi cách nhau vài ngày vẫn có thể là đồng thời.

    Hai bạn viết hai câu. Nếu bạn thứ hai đã đọc câu của bạn thứ nhất rồi mới viết, thì câu sau là “viết tiếp” — rõ ràng có trước có sau. Còn nếu hai bạn viết mà không ai đọc của ai, thì gọi là viết đồng thời, dù có cách nhau cả tuần. Nhớ nhé: “đồng thời” ở đây nghĩa là không ai biết về ai, chứ không phải cùng một giây.

    Detecting concurrent writes needs a definition that doesn't rest on physical time. Kleppmann uses the happens-before relation: operation B happened after A if B knows about A, depends on A, or builds upon A; if neither operation knows about the other, they are concurrent1. The definition comes straight from Leslie Lamport's 1978 paper, which constructs a partial ordering of events in a distributed system with no physical clock7. The crucial point: concurrent here does not mean “at the same instant” but neither knows about the other — two writes days apart can still be concurrent.

    Hệ quả thực tếKhi có nhiều bản sao cùng nhận ghi, mỗi bản phải giữ một số phiên bản riêng và theo dõi cả số phiên bản mình từng thấy từ các bản khác. Tập hợp các số đó là version vector. Client đọc sẽ nhận về mọi phiên bản không bị phiên bản nào khác vượt qua — Riak gọi chúng là sibling — và phải ghép chúng lại khi ghi tiếp.
    Ý chínhMỗi cuốn sổ phải nhớ mình đã thấy gì của các cuốn khác, thì mới biết ai viết tiếp ai.
    The practical upshotWhen several replicas accept writes, each must keep its own version number and track the version numbers it has seen from the others. That collection is a version vector. A reading client gets back every version not superseded by another — Riak calls them siblings — and must merge them on the next write.

    Ghép nghĩa là gì

    Kleppmann minh họa bằng giỏ hàng: hợp nhất hai phiên bản bằng phép hợp các món là đúng cho việc thêm, nhưng sai cho việc xóa — món đã bị xóa sẽ quay lại. Muốn xóa đúng, hệ phải để lại một dấu mộ (tombstone) ghi rằng món này đã bị bỏ đi. Bài báo Dynamo mô tả đúng hệ quả này khi nói về giỏ hàng của Amazon: nhờ cơ chế hòa giải, thao tác “thêm vào giỏ” không bao giờ bị mất — nhưng các món đã xóa có thể nổi trở lại5.

    Version vector không phải vector clock

    Hai thuật ngữ hay bị dùng lẫn. Carlos Baquero — một trong các tác giả của công trình về dotted version vector — viết hẳn một bài để tách bạch: chúng là hai cơ chế khác nhau dù cùng mang hình dạng một vector các bộ đếm18. Tài liệu Riak thừa nhận sự lẫn lộn này ngay trong sản phẩm: API HTTP vẫn dùng header X-Riak-Vclock kể cả khi hệ đang chạy dotted version vector15. Bản thân dotted version vector được đề xuất trong bài báo của Preguiça, Baquero, Almeida và cộng sự như một loại đồng hồ logic cho nhân bản lạc quan17, và Riak chuyển sang dùng nó từ phiên bản 2.014.

Ba ngộ nhận thường gặp về nhân bảnThree common misreadings of replication

“w + r > n là bảo đảm đọc thấy ghi mới nhất”"w + r > n guarantees reading the newest write"

Sai: đó là điều kiện để hai tập nút giao nhau, và Kleppmann liệt kê hàng loạt trường hợp biên nó vẫn đúng mà tính nhất quán vẫn hỏng — sloppy quorum, ghi đồng thời, ghi thất bại một phần, nút phục hồi từ bản sao cũ1. Hãy đọc nó như một điều kiện cần, kèm giám sát, chứ không như một lời hứa.

Wrong: it's the condition for the two node sets to overlap, and Kleppmann lists edge cases where it holds while consistency fails — sloppy quorums, concurrent writes, partially failed writes, nodes restored from old replicas1. Read it as a necessary condition backed by monitoring, not as a promise.

“LWW là cách hóa giải xung đột đúng đắn”"LWW is the correct way to resolve conflicts"

Sai: nó đạt hội tụ bằng cách xóa hẳn các ghi thua cuộc, dù những ghi đó đã được báo thành công. Nó chỉ an toàn khi mỗi khóa được ghi đúng một lần rồi bất biến1. Tài liệu Riak nói thẳng rằng dấu thời gian không phải cơ chế hóa giải đáng tin trong hệ phân tán15.

Wrong: it converges by permanently deleting the losing writes, even though they were acknowledged. It is safe only when each key is written once and thereafter immutable1. Riak's own documentation states that timestamps are not a reliable resolution mechanism in distributed systems15.

“Nhân bản = sao lưu”"Replication = backup"

Sai: bản sao đang phục vụ và đang bám theo leader, nên một lệnh DELETE nhầm hay một bản ghi hỏng sẽ được nhân bản trung thực sang mọi bản sao. Nhân bản chống lại mất máy; nó không chống lại thao tác sai — vốn là nguồn sự cố hàng đầu mà Chương 1 đã nêu.

Wrong: replicas are serving traffic and tracking the leader, so a mistaken DELETE or a corrupt row is faithfully replicated everywhere. Replication protects against losing a machine; it does not protect against a wrong operation — the leading incident source Chapter 1 named.

Tự đọc mìnhRead yourself

Soi chiến lược nhân bản của chính hệ bạnAudit your own replication strategy

Gần như mọi hệ có cơ sở dữ liệu đều đang nhân bản một thứ gì đó, kể cả khi đội phát triển chưa bao giờ gọi tên nó. Những câu hỏi dưới đây không đo hệ của bạn “phân tán” tới đâu; chúng soi xem bạn đã nói thành lời được các đánh đổi mà chương này bày ra hay chưa — và đặc biệt là những đánh đổi mà bạn đã ngầm chấp nhận mà không biết.

Thử soi một hệ thật mà bạn đang làm bằng mấy câu dưới. Không phải để xem nó to hay nhỏ, mà để xem bạn đã biết rõ nó chép dữ liệu kiểu gì và hỏng thì mất gì chưa.

Nearly every system with a database is replicating something, even if the team never named it. The questions below don't measure how “distributed” your system is; they test whether you can yet put into words the trade-offs this chapter lays out — especially the ones you've silently accepted without noticing.

Hệ của bạn thuộc họ nào: một lãnh đạo, nhiều lãnh đạo, hay không lãnh đạo?Câu hỏi nghe hiển nhiên nhưng rất hay bị trả lời sai, vì nhiều đội có cả ba trong cùng một kiến trúc: cơ sở dữ liệu chính chạy leader–follower, cache chạy kiểu khác, còn ứng dụng di động thì đang làm multi-leader mà không ai gọi tên. Hãy vẽ ra chứ đừng trả lời bằng trí nhớ.
Nhân bản của bạn đồng bộ hay bất đồng bộ — và bạn biết điều đó từ đâu?Phần lớn hệ mặc định bất đồng bộ: PostgreSQL chỉ chuyển sang đồng bộ khi người vận hành đặt synchronous_standby_names. Nếu bạn chưa từng bật nó, câu trả lời của bạn là “bất đồng bộ”, và bạn đã chấp nhận rằng một failover có thể làm mất những ghi đã báo thành công.
Khi leader chết, bạn mất bao nhiêu ghi — và số đó có được viết ra ở đâu không?Đây là câu hỏi trung tâm của failover. Nếu chưa ai trong đội trả lời được bằng một con số hoặc một khoảng, thì bạn chưa có cam kết bền vững, chỉ có hy vọng. Và hãy hỏi tiếp: có hệ nào ngoài cơ sở dữ liệu phụ thuộc vào những ghi có thể bị vứt không — cache, chỉ mục tìm kiếm, hàng đợi, kho phân tích?
Bạn chặn split brain bằng cơ chế gì, và timeout của bạn dựa trên số đo nào?“Chúng tôi dùng công cụ X” không phải câu trả lời — hãy nói rõ điều gì ngăn hai nút cùng nhận ghi, và chuyện gì xảy ra nếu cơ chế đó cũng hỏng. Về timeout: quá dài thì kéo dài gián đoạn, quá ngắn thì một đợt tải cao sẽ kích failover thừa và đẩy hệ vào vòng xoáy chuyển vai liên tục.
Bạn có đọc từ bản sao không — và giao diện nào sẽ vỡ nếu bản sao đó tụt hậu 30 giây?Hãy đi qua từng màn hình mà người dùng sửa xong là nhìn lại ngay. Những chỗ đó cần read-your-writes. Còn những chỗ người dùng đọc nhiều lần liên tiếp thì cần monotonic reads. Nếu load balancer của bạn rải đọc ngẫu nhiên, bạn đang thiếu cái thứ hai.
Độ trễ nhân bản của bạn có nằm trên bảng giám sát không, và ở percentile nào?“Eventually consistent” không phải một số đo. Đo p50 và p99 của độ trễ nhân bản, và đặt ngưỡng cảnh báo. Với hệ leaderless thì việc đo này khó hơn nhiều vì không có thứ tự ghi cố định để so — hãy biết trước rằng bạn đang ở trường hợp khó.
Nếu hai người sửa cùng một bản ghi ở hai nơi, hệ của bạn làm gì?Có ba câu trả lời có thể chấp nhận: hệ ngăn chuyện đó xảy ra (định tuyến mọi ghi của bản ghi ấy về một nơi), hệ giữ cả hai rồi hỏi lại, hoặc hệ có một luật hóa giải mà bạn viết ra và hiểu được. Câu trả lời “chắc là nó tự xử lý” nghĩa là bạn đang chạy LWW mà không biết.
Ở đâu trong hệ bạn đang bật last-write-wins, và bạn có ý thức rằng đó là mất dữ liệu không?Hãy kiểm tra cấu hình thật chứ đừng đoán: last_write_wins và allow_mult trong Riak, mức nhất quán và dấu thời gian mutation trong Cassandra, hoặc chính logic “ghi đè bản mới nhất” trong mã ứng dụng của bạn. LWW chỉ an toàn khi mỗi khóa được ghi đúng một lần rồi bất biến.
Bạn có nhầm nhân bản với sao lưu không?Thử một phép thử đơn giản: nếu ai đó chạy nhầm một lệnh xóa trên leader lúc 3 giờ chiều, bạn khôi phục bằng cách nào? Nếu câu trả lời là “chúng tôi có bản sao”, thì bạn chưa có câu trả lời — vì bản sao đã sao chép trung thực lệnh xóa đó rồi.
Hệ của bạn có mấy “cuốn sổ”, và ai được viết vào?Biết ai được viết thì mới biết lúc đụng nhau phải xử lý ra sao.
Người viết xong có nhìn thấy ngay chữ của mình không?Nếu không thấy, người dùng sẽ tưởng là mất và viết lại lần nữa.
Các cuốn sổ chép chậm hơn cuốn chính bao lâu — bạn có đo không?Nói “rồi cũng khớp” thì chưa đủ. Phải biết là mấy giây.
Hai người cùng sửa một dòng thì hệ giữ cả hai hay bỏ một?Nếu bỏ một, có ai biết là đã bỏ không?
Nếu ai đó xóa nhầm, bạn lấy lại bằng cách nào?Chép ra nhiều bản không cứu được chuyện xóa nhầm — vì bản nào cũng bị xóa theo.
Which family is your system: single-leader, multi-leader or leaderless?Obvious-sounding but often answered wrong, because many teams run all three at once: the main database is leader–follower, the cache is something else, and the mobile app is doing multi-leader that nobody named. Draw it; don't answer from memory.
Is your replication synchronous or asynchronous — and how do you know?Most systems default to asynchronous: PostgreSQL only becomes synchronous when an operator sets synchronous_standby_names. If you never turned it on, your answer is “asynchronous”, and you've accepted that a failover can lose acknowledged writes.
When the leader dies, how many writes do you lose — and is that number written down anywhere?This is the central failover question. If nobody on the team can answer with a number or a range, you have no durability commitment, only hope. Then ask: does any system outside the database depend on the writes that may be discarded?
What prevents split brain, and what measurement sets your timeout?“We use tool X” isn't an answer — say what stops two nodes accepting writes, and what happens if that mechanism also fails. Too long a timeout prolongs outages; too short and a load spike triggers needless failovers.
Do you read from replicas — and which screens break if one lags 30 seconds?Walk through every screen where a user edits and immediately looks again; those need read-your-writes. Screens read repeatedly need monotonic reads. If your load balancer spreads reads randomly, you're missing the second.
Is replication lag on your dashboard, and at which percentile?“Eventually consistent” is not a measurement. Track p50 and p99 of replication lag and set an alert. For leaderless systems this is much harder because there's no fixed write order to compare against — know in advance that you're in the hard case.
If two people edit the same record in two places, what does your system do?Three acceptable answers: it prevents that (routing all writes for the record to one place), it keeps both and asks, or it has a resolution rule you wrote and understand. “It probably handles it” means you're running LWW without knowing.
Where is last-write-wins enabled, and do you know that it means data loss?Check the actual configuration rather than guessing: last_write_wins and allow_mult in Riak, consistency levels and mutation timestamps in Cassandra, or your own “overwrite with the newest” logic. LWW is safe only when each key is written once and thereafter immutable.
Are you confusing replication with backup?A simple test: if someone runs a mistaken delete on the leader at 3pm, how do you recover? If the answer is “we have replicas”, you don't have an answer — the replicas faithfully copied that delete.
Kết luận cô đọngThe conclusion, distilled
Nhân bản không phải bài toán sao chép mà là bài toán thỏa thuận về sự thật giữa những máy không bao giờ nhìn thấy nhau cùng lúc. Một lãnh đạo mua sự đơn giản bằng một điểm chết và một nút thắt ghi; nhiều lãnh đạo mua sự sẵn sàng bằng xung đột mà bạn phải tự hóa giải; không lãnh đạo mua khả năng ghi gần như luôn thành công bằng việc bắt ứng dụng phải hiểu thế nào là đồng thời. Mọi công thức trong chương — kể cả w + r > n — đều là điều kiện có kèm trường hợp biên, chứ không phải lời hứa. Điều còn lại sau chương này không phải một kiến trúc để chọn, mà một thói quen: nói rõ bạn đang trả giá ở đâu.Chép dữ liệu ra nhiều nơi thì dễ. Khó là lúc có người sửa: sửa ở đâu, ai được sửa, và khi hai người sửa khác nhau thì nghe theo ai. Nhớ nhé: cách nào cũng phải trả giá ở một chỗ — chỉ cần bạn biết rõ mình đang trả ở chỗ nào.Replication is not a copying problem but a problem of agreeing on truth between machines that never see each other at the same moment. Single-leader buys simplicity with a single point of failure and a write bottleneck; multi-leader buys availability with conflicts you must resolve yourself; leaderless buys near-always-accepted writes by making the application understand what concurrent means. Every formula in the chapter — w + r > n included — is a condition with edge cases, not a promise. What survives this chapter is not an architecture to pick but a habit: say plainly where you are paying.