Hệ thống phân tán đo thật cho backend
Sê-ri 13 phần về những thứ xảy ra khi dữ liệu và tính toán nằm trên nhiều máy: mạng không đáng tin, đồng hồ lệch, khoá phân tán, bầu chủ, Raft, sao chép, consistent hashing, 2PC và saga, CRDT, phát hiện lỗi, sharding, phân vùng mạng. Mỗi phần tái hiện vấn đề bằng container thật (etcd, PostgreSQL, Redis, Go, tc/iptables), đo bằng số rồi mới kết luận.
4/13 phần đã đăng
Lập trình
1
Mạng không đáng tin: đo thật bằng tc netem giữa hai container — mất gói 30% vẫn 137/200 thành công nhưng p99 lên 420 ms, và 10 đơn hàng được tạo mà client tưởng là lỗi
Mở đầu sê-ri hệ thống phân tán bằng cách làm hỏng mạng có chủ đích: tc netem trên một container Go gọi sang container khác. Trễ 50 ms thành 107 ms mỗi request vì bắt tay TCP; mất gói 5% làm p99 nhảy từ 0,3 lên 209 ms vì TCP truyền lại theo bậc 200/400/800 ms; mất 30% vẫn 137/200 thành công nhưng tối đa 829 ms. Phép đo quan trọng nhất: dưới mất gói 30%, client thấy 125 thành công và 75 lỗi, server đếm 135 đơn đã tạo — 10 request client tưởng lỗi nhưng server đã làm. Tải 1 MB ở mất gói 15% không lỗi lần nào, chỉ chậm gấp 160 lần. Phân vùng 3 giây được phát hiện sau đúng một timeout.
22/09/2026
· 8 phút đọc
2
Đồng hồ và thứ tự: đo thật trên ba tiến trình — lệch đồng hồ 1 ms đã đủ để đồng hồ tường xếp 300/900 cặp gửi→nhận ngược nhân quả, Lamport và vector thì 0
Ba tiến trình Go gửi 900 thông điệp cho nhau qua HTTP, mỗi sự kiện ghi cả đồng hồ tường, Lamport và vector clock. Cặp gửi→nhận là nhân quả chắc chắn, vậy mà chỉ cần một node lệch đồng hồ 1 ms — nhỏ hơn mọi lời hứa của NTP — đồng hồ tường đã xếp 300/900 cặp ngược: nhận trước gửi. Lamport và vector sai 0 lần ở mọi độ lệch. Trong 270.000 cặp sự kiện ở hai node khác nhau, vector nhận ra 1.269 cặp đồng thời, còn Lamport vẫn gán trước sau cho 1.109 trong số đó. Và LWW theo đồng hồ tường: node có đồng hồ nhanh 20 ms thắng 200/200 dù ghi trước, bản cũ đè bản mới.
22/09/2026
· 9 phút đọc
3
Khoá phân tán: đo thật SET NX kèm TTL — tiến trình treo quá TTL làm mất 100/100 cập nhật im lặng, DEL trần xoá khoá của người khác 100/100 lần, và fencing token chặn đủ 100 bản ghi cũ
Khoá bằng Redis SET NX PX với TTL 300 ms, vùng găng là đọc-cộng-ghi số dư trong PostgreSQL. Tiến trình A lấy khoá rồi treo 500 ms, khoá hết hạn, B lấy khoá và ghi; A tỉnh dậy ghi đè: mất 100/100 cập nhật của B, không lỗi, không log. Thêm fencing token tăng dần cấp cùng lúc với khoá và kiểm ở tầng lưu trữ: 100 bản ghi cũ bị từ chối, A biết mà làm lại, số dư đúng 200. Mở khoá bằng DEL trần: A xoá khoá của B, C vào khi B đang làm, 100/100 vòng; so chủ trước khi xoá: 0. Redlock 3 node: khi một Redis chết, một Redis đơn 0/200, Redlock 200/200 với giá một timeout mỗi lần.
22/09/2026
· 8 phút đọc
4
Bầu chủ với etcd lease: đo thật trên cụm 3 node — leader chết đột ngột thì chờ đúng TTL (10,01 s với TTL 10 s), từ nhiệm thì 1 ms, và leader cũ tỉnh dậy ghi thẳng 50/50 lần trừ khi dùng Txn
Ba ứng viên dùng etcd concurrency.Election trên lease. Giết leader bằng cách đóng kết nối không revoke: thời gian có leader mới bằng đúng TTL — 1,98 s, 2,98 s, 10,01 s cho TTL 1, 3, 10 giây (etcd đẩy TTL 1 s lên sàn 2 s). Từ nhiệm có chủ ý: 1 ms. Kịch bản đáng sợ hơn: leader A bị treo 4 s với TTL 2 s, B lên thay lúc 2,30 s, A tỉnh dậy lúc 4,07 s vẫn tưởng mình là leader và ghi 50 bản ghi — Put thẳng thành công 50/50, hai leader cùng ghi; Txn kèm điều kiện khoá leader còn tồn tại bị etcd từ chối 50/50. Chi phí: TTL 1 s là 1 keepalive/giây mỗi leader.
22/09/2026
· 7 phút đọc
Còn 9 phần nữa sẽ lần lượt được đăng.