Hệ thống Dữ liệu Phân tán/Linearizability — nhất quán mạnh và cái giá của nó
33/38
Bài 33 / 38~12 phútNhất quán & Đồng thuậnMiễn phí lượt xem

Linearizability — nhất quán mạnh và cái giá của nó

Linearizability: hệ hành xử như thể chỉ có một bản dữ liệu, mọi thao tác thấy giá trị mới nhất. Định nghĩa, vì sao mạnh, và vì sao đắt.

TL;DR: Linearizability (nhất quán mạnh) đảm bảo hệ hành xử như thể chỉ có MỘT bản dữ liệu, dù nhân bản trên nhiều node: mỗi thao tác trông như xảy ra tức thời tại một điểm giữa lúc client gửi lệnh và nhận kết quả, và các điểm đó tôn trọng thời gian thực. Hệ quả cốt lõi là recency: đã thấy giá trị mới thì mọi lần đọc sau không bao giờ thấy giá trị cũ. Khác serializability (thứ tự giao dịch, không đòi khớp real-time) — linearizability chỉ một object nhưng có ràng buộc real-time. Cái giá: cần phối hợp giữa bản sao, chậm hơn và mất khả dụng khi mạng phân mảnh — nền cho CAP ở bài sau.

Bạn vừa đổi mật khẩu, hệ báo "thành công". Bạn đăng nhập lại ngay bằng mật khẩu mới — nhưng bị từ chối, vì lần đăng nhập trúng một bản sao chưa kịp nhận cập nhật. Một hệ linearizable cấm đúng tình huống này: đã ghi xong và được xác nhận thì mọi lần đọc sau phải thấy giá trị mới. Đây là đảm bảo mạnh nhất về "mọi người thấy cùng một dữ liệu mới nhất".

Bài này định nghĩa chính xác linearizability, mổ cơ chế khiến nó mạnh (recency qua real-time order), phân biệt nó với serializability (hai khái niệm rất hay bị trộn), và chỉ ra cái giá — mở đường cho CAP và consensus ở các bài sau.

1. Analogy — bảng tỉ số duy nhất trên sân vận động

Trong một trận bóng, cả sân nhìn chung một bảng tỉ số điện tử. Khi trọng tài công nhận bàn thắng, bảng đổi từ 1-0 sang 2-0. Kể từ khoảnh khắc đó, không một ai — khán giả A ở khán đài Bắc, khán giả B ở khán đài Nam — còn thấy 1-0 nữa. Không có chuyện người này thấy 2-0 rồi người kia vẫn thấy 1-0: chỉ có một bảng, một sự thật, và nó nhảy tức thời tại một thời điểm.

Linearizability đem đúng ảo giác đó vào hệ phân tán nhiều bản sao: dù bên trong dữ liệu nằm trên năm máy, người dùng bên ngoài thấy như thể chỉ có một bảng tỉ số.

Bảng tỉ sốHệ dữ liệu linearizable
Một bảng cả sân cùng nhìnẢo giác "một bản dữ liệu duy nhất"
Đổi 1-0 sang 2-0 tức thờiMỗi thao tác có một điểm xảy ra tức thời
Đổi xong, không ai thấy 1-0 nữaRecency — không quay lại giá trị cũ
Thứ tự bàn thắng theo đồng hồ trận đấuThứ tự thao tác tôn trọng thời gian thực
💡 Cách nhớ

Linearizable = "làm như chỉ có một bản". Ghi xong được xác nhận → mọi lần đọc sau thấy ngay, không bao giờ tụt về giá trị cũ.

2. Định nghĩa chính xác — một điểm giữa invoke và response

Mỗi thao tác trong hệ thực chất kéo dài một khoảng thời gian: từ lúc client gửi lệnh (invoke) tới lúc nhận kết quả (response) — vì lệnh phải đi qua mạng, tới bản sao, xử lý, rồi về. Linearizability (Herlihy & Wing, 1990) đòi: tồn tại một cách gán cho mỗi thao tác một điểm duy nhất (linearization point) nằm trong khoảng của nó, sao cho:

  1. Nếu ta sắp các thao tác theo các điểm đó thành một dãy tuần tự, kết quả hợp lệ như một bản dữ liệu đơn (đọc trả về giá trị của lần ghi gần nhất trước nó).
  2. Thứ tự các điểm tôn trọng real-time: nếu thao tác X kết thúc (response) trước khi thao tác Y bắt đầu (invoke), thì điểm của X phải đứng trước điểm của Y.

Điều kiện (2) là thứ khiến linearizability mạnh hơn các mô hình khác: nó gắn thứ tự logic với thời gian thực. Hệ quả trực tiếp là recency guarantee — một khi lần ghi x=2 đã response, mọi lần đọc bắt đầu sau đó phải thấy 2 (hoặc mới hơn), không được thấy 1.

Từ khoá: single-object, single-operation

Linearizability là đảm bảo trên một object, mỗi thao tác đơn lẻ (một read, một write, một compare-and-set). Nó không nói gì về gộp nhiều thao tác thành một giao dịch nguyên khối — đó là địa hạt của serializability (mục 4).

3. Cơ chế — worked example: lịch sử này có linearizable không?

Cách chắc nhất để "thấy" định nghĩa là chấm một lịch sử thao tác cụ thể. Xét một thanh ghi x (khởi tạo 0), ba client thao tác đồng thời. Mỗi thanh ngang là khoảng invoke → response:

Thoi gian ---->

A:  write(x=1) |=========|                       (ghi 1)
B:            |===== read -> ? =====|             (doc, chong len ghi cua A)
C:                          |== read -> ? ==|     (doc, bat dau SAU khi A xong)

Ta lần theo hai điều kiện của mục 2 để chấm:

  • C bắt đầu SAU khi A response → theo real-time order, điểm của write(x=1) phải đứng trước điểm của C. Vậy C bắt buộc đọc 1. Nếu C đọc 0KHÔNG linearizable (vi phạm recency).
  • B chồng lên A (concurrent) → điểm của B có thể rơi trước hoặc sau điểm ghi. Nên B đọc 0 hoặc 1 đều hợp lệ — miễn là nhất quán với ràng buộc tiếp theo.
  • Ràng buộc chốt: không được lùi. Nếu B đọc 1 (nghĩa là điểm ghi đã "xảy ra" trước điểm của B), thì C — bắt đầu còn sau nữa — không thể đọc 0. Một khi giá trị mới lộ ra, nó không quay lại được.

Ta tìm được một cách đặt điểm hợp lệ (ví dụ: điểm ghi giữa khoảng A, B đọc 1, C đọc 1) → lịch sử này linearizable. Nhưng nếu quan sát thực tế là B=1, C=0, không cách đặt điểm nào cứu được → không linearizable.

flowchart LR
  W["write x=1<br/>diem giua khoang A"] --> B["read B -> 1<br/>(diem sau diem ghi)"]
  B --> C["read C -> 1<br/>BAT BUOC (C sau A)"]
  X["read C -> 0"] -.->|"vi pham recency"| FAIL["KHONG linearizable"]

Điểm mấu chốt của cơ chế: mỗi thao tác "nhảy" từ giá trị này sang giá trị kia tại một thời điểm, và cả hệ đồng ý về thứ tự các bước nhảy đó — đúng như bảng tỉ số. Để đạt điều này với nhiều bản sao, hệ phải phối hợp: một lần đọc không thể trả lời từ một bản sao cũ mà chưa chắc mình có giá trị mới nhất; nó phải đồng bộ với các bản khác (hoặc đọc qua leader/quorum có đọc-sửa). Chính bước phối hợp đó là nguồn của cái giá ở mục 5.

💡 Nối về CAS đã học
Nhớ lại Atomic & CAS

compare-and-set (CAS) bạn học ở concurrency là một thao tác linearizable trên một biến: "nếu giá trị đang là A thì đặt thành B" xảy ra nguyên khối, tại một điểm, không ai chen giữa. Linearizability là chính khái niệm đó nâng lên cấp hệ phân tán nhiều bản sao — và cũng vì thế nó cần phần cứng/quorum để bảo đảm tính nguyên khối, không "miễn phí".

4. Linearizability KHÁC serializability thế nào?

Hai từ nghe giống nhau nhưng là hai trục hoàn toàn khác — trộn chúng là lỗi kinh điển.

LinearizabilitySerializability
Phạm viMột object, một thao tác đơnNhiều object, nhiều thao tác gộp thành giao dịch
Đảm bảoThứ tự thời gian thực (recency)Kết quả như thể các giao dịch chạy tuần tự (thứ tự nào cũng được)
Ràng buộc real-time — X xong trước Y bắt đầu thì X trướcKhông — không đòi khớp đồng hồ vật lý
Thuộc vềMô hình nhất quán (consistency) của replicationMức isolation của transaction

Nói ngắn: serializability (đã học ở Module 4) bảo "các giao dịch không giẫm chân nhau, kết quả như một lịch tuần tự nào đó" — nhưng lịch nào thì không cần khớp thời gian thực. Linearizability bảo "mỗi thao tác lên một object thấy giá trị mới nhất theo đồng hồ tường". Một hệ có thể serializable mà không linearizable (ví dụ Serializable Snapshot Isolation — SSI — đọc từ một snapshot nhất quán chứ không phải giá trị mới nhất), và ngược lại. Khi cần cả hai — giao dịch tuần tự khớp thời gian thực — ta nói strict serializability.

5. Cái giá — vì sao nhất quán mạnh thì đắt?

Recency không miễn phí. Để mọi lần đọc thấy giá trị mới nhất, một bản sao không được tự tin trả lời từ dữ liệu cục bộ có thể đã cũ; nó phải phối hợp — hỏi leader, hoặc đọc/ghi qua quorum có đồng bộ, hoặc chạy một giao thức consensus. Ba hệ quả:

  1. Chậm hơn (latency): mỗi thao tác gánh thêm ít nhất một vòng đi-về mạng để phối hợp, thay vì trả lời ngay từ bản gần nhất.
  2. Kém chịu lỗi khi mạng phân mảnh (availability): nếu một bản sao bị cắt khỏi phần còn lại (network partition), nó không thể biết chắc mình có giá trị mới nhất → để giữ linearizability, nó phải từ chối phục vụ thay vì trả lời liều. Đây chính là đánh đổi C-vs-A của định lý CAP — bài 03.
  3. Không phải lúc nào cũng cần: rất nhiều tính năng (số like, feed, gợi ý) chịu được dữ liệu trễ vài giây. Áp linearizability lên chúng là trả giá đắt cho một đảm bảo không dùng đến.

Vì vậy linearizability là công cụ dành cho chỗ thật sự cần "một sự thật mới nhất": khoá phân tán, bầu leader, số dư tài khoản khi ràng buộc cứng, uniqueness constraint. Những mức yếu hơn mà rẻ hơn (như causal consistency, bài 02) đủ cho phần còn lại.

6. Pitfall — nhầm linearizability với serializability, và "quorum là đủ"

Pitfall — hai nhầm lẫn hay đi cùng nhau
❌ "He serializable roi thi doc luon thay gia tri moi nhat"
   -> SAI: serializability khong rang buoc real-time; mot giao dich
   co the doc snapshot cu van "tuan tu hop le". Recency la linearizability.

❌ "Quorum read/write (w + r > n) => linearizable"
   -> KHONG du: quorum chong mat ghi, nhung do dinh thoi (doc chay song song
   voi ghi chua kip lan toi) van co the tra ve gia tri cu. Can them dong bo
   (read-repair dong bo / doc qua leader) moi dat linearizable.

Hệ quả: đội tin "dùng quorum là nhất quán mạnh" rồi gặp bug hiếm — một lần đọc ngay sau ghi thỉnh thoảng thấy giá trị cũ, cực khó tái hiện vì phụ thuộc định thời mạng.

Hướng đúng: tách bạch hai câu hỏi. "Các giao dịch có tuần tự không?" → serializability (isolation). "Một thao tác đơn có thấy giá trị mới nhất theo thời gian thực không?" → linearizability. Chỉ bật linearizability ở chỗ cần recency thật (khoá, leader, uniqueness); đừng mặc định nó cho mọi read.

7. 📚 Đào sâu — nguồn gốc & tài liệu

📚 Đào sâu — Linearizability

Ghi chú: Herlihy & Wing viết cho object đồng thời trong một máy (memory), nhưng định nghĩa áp thẳng cho hệ phân tán — đó là lý do CAS (một máy) và linearizable register (nhiều node) là cùng một ý ở hai quy mô.

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

  • Bài 02 — Causal consistency: mức yếu hơn ngay dưới linearizability — bỏ ràng buộc total order/real-time để đổi lấy khả dụng khi partition; đọc để thấy "rẻ hơn" nghĩa là gì.
  • Bài 03 — CAP đúng nghĩa: "kém khả dụng khi partition" ở mục 5 chính là nửa C của CAP — bài 03 hình thức hoá đánh đổi đó.
  • Module 2 — Leaderless replication: quorum w + r > n được mổ ở đó; bài này chỉ ra vì sao quorum một mình chưa đạt linearizability.
  • Module 4 — Anomaly & serializability: trục còn lại (isolation của giao dịch) — đối chiếu để không trộn hai khái niệm.

9. Tóm tắt

  • Linearizability = ảo giác "chỉ một bản dữ liệu": mỗi thao tác trông như xảy ra tức thời tại một điểm giữa invoke và response.
  • Đảm bảo cốt lõi là recency + real-time order: X xong trước Y bắt đầu thì điểm của X đứng trước; đã thấy giá trị mới thì không lùi về cũ.
  • Chấm một lịch sử = tìm cách đặt linearization point cho mọi thao tác thoả cả hai điều kiện; đọc sau một ghi đã xong thì bắt buộc thấy giá trị mới.
  • Khác serializability: một object + real-time (linearizability) vs nhiều giao dịch + "tuần tự nào đó" (serializability); cả hai = strict serializability.
  • Cái giá: phối hợp giữa bản sao → chậm + mất khả dụng khi partition; chỉ nên bật ở chỗ cần recency thật (khoá, leader, uniqueness).
  • Pitfall: nhầm với serializability; tưởng quorum một mình là đủ cho nhất quán mạnh.

10. Tự kiểm tra

Tự kiểm tra
Q1
Vì sao ràng buộc "real-time order" (thao tác X kết thúc trước khi Y bắt đầu thì điểm của X đứng trước) là thứ khiến linearizability mạnh hơn các mô hình khác? Nêu hệ quả trực tiếp của nó.

Vì nó gắn thứ tự logic với thời gian thực (đồng hồ tường), không chỉ với quan hệ nhân quả hay một thứ tự tuần tự trừu tượng nào đó. Các mô hình yếu hơn (như causal) cho phép hai node thấy các thao tác *đồng thời* theo thứ tự khác nhau; linearizability thì không — nếu một thao tác đã kết thúc trước khi thao tác khác bắt đầu, mọi node phải đồng ý thao tác trước đứng trước.

Hệ quả trực tiếp là recency: một khi lần ghi x=2 đã trả kết quả, mọi lần đọc bắt đầu sau đó phải thấy 2 (hoặc mới hơn), không bao giờ thấy 1. Đây là điều làm "đổi mật khẩu xong đăng nhập ngay bị từ chối" không thể xảy ra dưới linearizability.

Q2
Một thanh ghi x khởi tạo 0. Client A chạy write(x=1). Client C bắt đầu read(x) sau khi A đã nhận kết quả "ghi xong". C đọc ra 0. Hệ có linearizable không? Giải thích bằng cơ chế điểm.

Không linearizable. Vì C bắt đầu sau khi write(x=1) đã response, điều kiện real-time buộc linearization point của lần ghi đứng trước điểm của C. Trong một dãy tuần tự hợp lệ, một lần đọc phải trả về giá trị của lần ghi gần nhất trước nó — ở đây là 1.

C đọc 0 nghĩa là nó thấy trạng thái trước lần ghi, tức điểm của C đứng trước điểm ghi — mâu thuẫn với ràng buộc real-time. Không có cách đặt điểm nào cứu được lịch sử này, nên nó vi phạm recency và không linearizable.

Q3
Phân biệt linearizability và serializability. Cho một ví dụ hệ đạt cái này mà không đạt cái kia.

Serializability là mức isolation của giao dịch: nhiều thao tác trên nhiều object gộp lại, kết quả như thể các giao dịch chạy tuần tự theo một thứ tự nào đó — nhưng không đòi thứ tự đó khớp thời gian thực. Linearizability là mô hình nhất quán của một object, một thao tác đơn: mỗi thao tác thấy giá trị mới nhất theo real-time (recency).

Ví dụ đạt serializable mà không linearizable: một hệ dùng Serializable Snapshot Isolation (SSI) — mỗi giao dịch đọc từ một snapshot nhất quán tại một thời điểm, và SSI phát hiện + chặn xung đột write skew nên kết quả thật sự tuần tự hợp lệ (khác snapshot isolation trần, vốn cho phép write skew nên không serializable). Dù vậy một giao dịch vẫn có thể đọc từ snapshot cũ trong khi đã có ghi mới hơn commit sau đó — vi phạm recency của linearizability. Muốn cả hai cùng lúc, cần strict serializability.

Q4
Vì sao quorum read/write (w + r > n) một mình thường KHÔNG đủ để đạt linearizability? Cần thêm gì?

Quorum w + r > n đảm bảo tập node đọc và tập node ghi giao nhau ít nhất một node → lần đọc chạm ít nhất một bản có giá trị mới. Nhưng nó chống mất giá trị mới, không chống lệch định thời: một lần đọc chạy song song với một lần ghi chưa lan tới đủ node vẫn có thể gom được đa số bản và trả về giá trị cũ, dù một lần đọc khác cùng lúc đã thấy giá trị mới — hai lần đọc "lùi tới lùi lui".

Để đạt linearizable cần thêm cơ chế đồng bộ: read-repair đồng bộ (lần đọc buộc cập nhật các bản cũ rồi mới trả lời) hoặc định tuyến thao tác qua một leader/consensus để có một điểm sắp thứ tự duy nhất. Không có bước này, quorum chỉ cho "eventual/read-your-writes" tuỳ cấu hình, chưa phải recency chặt.

Q5
Cho hai tính năng: (a) đếm số lượt thích một bài viết, (b) một khoá phân tán quyết định "ai được ghi vào file này". Cái nào cần linearizability, cái nào không? Vì sao — nhìn từ cái giá ở mục 5.

(b) khoá phân tán cần linearizability; (a) đếm like thì không. Khoá quyết định "chỉ một node được vào" — nếu hai node cùng lúc thấy khoá "trống" (vì đọc hai bản sao lệch nhau) thì cả hai cùng ghi → hỏng dữ liệu. Đây đúng là chỗ cần một sự thật mới nhất duy nhất, đáng trả giá phối hợp + mất khả dụng khi partition.

Đếm like chịu được trễ vài giây và hội tụ dần: thấy 1000 hay 1001 like trong khoảnh khắc không gây sai nghiệp vụ. Áp linearizability lên nó là trả cái giá của mục 5 (chậm + kém khả dụng) cho một đảm bảo không cần đến — nên dùng mức yếu hơn, rẻ hơn (eventual/causal). Nguyên tắc: bật recency đúng chỗ cần, đừng mặc định cho mọi thao tác.

Bài tiếp theo: Causal consistency — yếu hơn nhưng đủ, rẻ hơn

Bài này có giúp bạn hiểu bản chất không?

Hỏi đáp về bài này

Chưa có câu hỏi

Đặt câu hỏi

Có gì chưa rõ trong bài? Đặt câu hỏi đầu tiên — câu trả lời từ cộng đồng giúp bạn (và người sau).

Đặt câu hỏi đầu tiên