Chuyển đến nội dung chính
  1. Bài viết/

Bài 06: Chuyển pha — vì sao hệ thống quá tải đóng băng thay vì chậm dần đều

Vì sao lượng việc làm được không giảm đều theo tải mà rơi thẳng đứng, vì sao hệ thống sau khi nghẽn xử lý được ít hơn cả lúc ngay trước khi nghẽn, và tín hiệu nào xuất hiện trong telemetry trước cú sập — đủ sớm để kịp làm gì đó.

I. Tóm tắt #

Trực giác tuyến tính dự đoán: tải tăng 10% thì độ trễ tăng cỡ 10%. Vật lý của chuyển pha và lý thuyết hàng đợi dự đoán điều khác hẳn: hệ thống chịu tải đi qua một cú nhảy trạng thái, với ba chữ ký đo được. Một — vách đá goodput: lượng việc làm xong có ích không giảm dần, nó rơi thẳng đứng. Hai — capacity drop: sau khi nghẽn, hệ xử lý được ít hơn cả mức nó vừa đạt ngay trước đó, và trạng thái tệ hơn ấy tự duy trì. Ba — critical slowing down: độ dao động của độ trễ tăng lên trước khi giá trị trung bình kịp nhúc nhích, nghĩa là cú sập quan sát được từ trước. Hệ quả kỹ thuật: định nghĩa sức chứa tại điểm gãy của đường cong độ trễ thay vì đỉnh benchmark, chĩa monitoring vào độ dao động thay vì giá trị trung bình, và khi hệ đã kẹt thì xả tải thật sâu thay vì gọt từng chút một.

II. Vấn đề: cái mà mô hình tuyến tính không có chỗ để mô tả #

Ngụy biện tuyến tính phát biểu: phản ứng của hệ thống là một hàm trơn theo tải. Service chạy 80% CPU nghĩa là còn 20% dự phòng; traffic tăng 10% thì độ trễ tăng cùng bậc; nếu quá tải thật thì hệ chậm lại tương ứng, giảm tải là hết đau. Mọi ngưỡng cảnh báo đặt trên độ trễ trung bình hay tỉ lệ lỗi đều mã hóa niềm tin này.

Hai quan sát từ production đập vỡ bức tranh đó.

Quan sát thứ nhất, quy mô nhỏ. Một service cấu hình connection pool 10 kết nối (HikariCP, maximumPoolSize=10) chạy êm nhiều tháng liền. Nhu cầu đồng thời leo dần lên 200. CPU vẫn vừa phải, memory đi ngang, không chuông báo nào kêu — vậy mà p95 vọt lên 5 giây và đứng nguyên ở đó. Có đội đã mất ba ngày truy lùng lớp lỗi này; bản fix, khi tìm ra, mất mười phút. Nút thắt là một trần đồng thời cứng, vô hình trước mọi metric tài nguyên mà tư duy tuyến tính cho là quan trọng.

Quan sát thứ hai, quy mô lớn. Tháng 10 năm 1986, đường truyền giữa Lawrence Berkeley Lab và UC Berkeley — cách nhau khoảng 400 yard qua ba router — rớt từ 32 kbps thông lượng hiệu dụng xuống quãng 40 bps. Một nghìn lần, không sợi cáp nào đứt (Jacobson & Karels, 1988). Tải đầu vào tăng theo phần trăm; đầu ra hữu ích sập ba bậc độ lớn, và mạng đứng nguyên trong trạng thái sập cho tới khi chính giao thức bị thay đổi.

Hai dấu hiệu đi cùng nhau — đầu ra nhảy gián đoạn theo đầu vào, và trạng thái suy sụp tự duy trì sau khi tác nhân đã qua — có tên trong vật lý: đó là chữ ký của một chuyển pha bậc nhất. Mô hình tuyến tính, về mặt cấu trúc, không có chỗ nào để biểu diễn hiện tượng này.

III. Khung lý thuyết #

Hai kiểu đổi trạng thái, và vì sao cần phân biệt #

Trước khi vào công thức, đây là điều duy nhất cần nhớ: có hai cách một hệ đổi trạng thái, và chúng hỏng theo hai kiểu khác nhau.

  • Đổi trạng thái liên tục. Giống thanh sắt nguội dần rồi từ từ nhiễm từ: không có mốc nào để chỉ tay nói “chính lúc này”. Hệ suy giảm dần, và bạn có thời gian phản ứng.
  • Đổi trạng thái gián đoạn. Giống nước đóng băng: ở $1^\circ\mathrm{C}$ vẫn là nước, xuống dưới $0^\circ\mathrm{C}$ thì thành đá, không có trạng thái nào ở giữa. Kèm theo đó là hai đặc điểm quan trọng với bài này: hệ có thể kẹt ở trạng thái sai (nước tinh khiết có thể lạnh dưới $0^\circ\mathrm{C}$ mà vẫn lỏng, cho tới khi một hạt bụi rơi vào và toàn khối đông cứng tức thì), và quay lại không dễ như đi tới (đun đá lên $1^\circ\mathrm{C}$ không làm nó tan ngay).

Landau (1937) mô tả sự khác biệt đó bằng một đại lượng gọi là tham số trật tự $\eta$ — một con số duy nhất tóm tắt hệ đang ở pha nào, bằng 0 ở pha hỗn loạn và khác 0 ở pha có trật tự. Với hệ thống phần mềm, $\eta$ chính là tỉ lệ giữa lượng việc làm xong có ích và lượng việc nhận vào.

Cách Landau viết ra sự khác biệt là qua năng lượng tự do $F$ — hiểu nôm na là “chi phí” mà hệ phải trả để ở một trạng thái nào đó; hệ luôn tự trôi về trạng thái chi phí thấp nhất, như hòn bi lăn xuống đáy thung lũng:

$$ F(T, \eta) = F_0(T) + a(T)\,\eta^2 + b(T)\,\eta^4 + \mathcal{O}(\eta^6) $$

Đọc bằng lời: chi phí của hệ được viết thành một đa thức theo tham số trật tự, và hình dạng của đa thức đó quyết định hệ hỏng theo kiểu nào. Khi các hệ số cho ra một thung lũng duy nhất, hòn bi trượt dần — đó là đổi trạng thái liên tục. Khi chúng cho ra hai thung lũng ngăn cách bởi một quả đồi, hòn bi phải nhảy từ thung lũng này sang thung lũng kia — đó là đổi trạng thái gián đoạn, và quả đồi ở giữa chính là lý do hệ kẹt lại ở thung lũng sai.

Hình ảnh hai thung lũng là thứ cần mang theo suốt phần còn lại của bài. Toàn bộ chuyện “hệ thống sập rồi không tự bò dậy” chỉ là: nó đã rơi xuống thung lũng thứ hai, và tải giảm không đủ để nó leo ngược qua quả đồi.

Điểm phân kỳ của hàng đợi #

Kleinrock (1975) đưa ra phiên bản quen thuộc nhất với kỹ sư. Với một hàng đợi đơn giản có tốc độ xử lý $\mu$ và utilization $\rho = \lambda/\mu$ (tải đến chia cho năng lực xử lý), thời gian chờ trung bình là:

$$ W = \frac{1}{\mu\,(1 - \rho)}, \qquad \frac{dW}{d\rho} = \frac{1}{\mu\,(1-\rho)^2} $$

Đọc bằng lời: thời gian chờ không tỉ lệ với tải, nó tỉ lệ với nghịch đảo của phần năng lực còn trống. Còn trống một nửa thì chờ gấp đôi mức nền; còn trống 5% thì chờ gấp hai mươi lần.

Công thức thứ hai là đạo hàm, và nó mới là chỗ bẻ gãy trực giác: độ dốc tại $\rho = 0{,}8$ đã gấp $6{,}25$ lần độ dốc tại $\rho = 0{,}5$. Nói cách khác, mỗi 1% utilization tăng thêm ở vùng 80% đắt gấp sáu lần so với ở vùng 50%. “Còn 20% headroom” không phải một khoản dự phòng tuyến tính; nó là vách dựng đứng của một đường hyperbola.

Nhưng mô hình cổ điển này dự đoán một sự phân kỳ êm đềm — việc vẫn hoàn thành, chỉ muộn hơn. Hệ thống thật không có lựa chọn đó: buffer hữu hạn, client có timeout, và chi phí điều phối tăng theo tranh chấp. Ba thứ đó biến đường tiệm cận trơn tru thành cú rơi gián đoạn của đầu ra hữu ích. Mục IV làm phép suy diễn từng bước; phiên bản ngắn: đường cong Kleinrock mô tả đoạn tiến vào chuyển pha, còn hình ảnh hai thung lũng mô tả điều xảy ra tại đó.

Một ranh giới phải vạch rõ #

Chuyển pha chính xác về mặt toán học chỉ tồn tại khi số phần tử tiến ra vô hạn (Goldenfeld 1992; Yang–Lee 1952). Cụm 200 node, hay kể cả 18.000 node của Roblox, vẫn là hệ hữu hạn: đường cong của nó là một khúc cua rất dốc, không phải một điểm gãy thật, và nó mượt hơn hình mẫu lý thuyết. Stumpf & Porter (2012) bổ sung lời cảnh báo song song: đừng vội tuyên bố tính phổ quát từ dữ liệu hữu hạn nhiều nhiễu.

Vì vậy bài này dùng “chuyển pha” như một mô hình xấp xỉ kiểm chứng được bằng số liệu — ba dự đoán của nó (hai nhánh cùng tồn tại, capacity drop, tín hiệu cảnh báo sớm) đều đối chiếu được với đo đạc thật — chứ không phải một tuyên bố về kỳ dị toán học trên phần cứng production.

Ký hiệu dùng trong bài. $\eta$ — tham số trật tự (tỉ lệ việc làm xong có ích); $F$ — năng lượng tự do, hiểu như chi phí của một trạng thái; $\lambda$ — tốc độ request đến; $\mu$ — tốc độ xử lý; $\rho = \lambda/\mu$ — utilization; $W$ — thời gian chờ trung bình; $\rho_c$ — utilization tới hạn; $T_o$ — timeout phía client; $G$ — goodput; $\kappa$ — tốc độ hồi phục của hệ (Mục VI); $\alpha$ — hệ số tự tương quan trễ bậc 1.

IV. Quy đổi sang kiến trúc phần mềm #

Giá trị của ngôn ngữ vật lý ở đây không phải trang trí. Nó cung cấp đúng ba khái niệm mà lý thuyết hàng đợi cổ điển không có: hai trạng thái ổn định song song, mầm kích hoạt, và capacity drop.

Khái niệm trong vật lýTương ứng trong hệ thống thật
Núm vặn từ bên ngoài (nhiệt độ, mật độ xe)Tải nhận vào: request mỗi giây, mức chiếm dụng CPU hay connection pool
Tham số trật tự ($\eta$)Tỉ lệ goodput trên tải nhận vào; mức đầy hàng đợi. Gần 1 ở pha khỏe, rơi về gần 0 sau chuyển pha
Đổi trạng thái gián đoạnVách đá goodput: đầu ra hữu ích rơi thẳng đứng; thời gian phản hồi vượt mọi timeout đã cấu hình
Kẹt ở trạng thái sai (nước lạnh dưới 0°C vẫn lỏng)Vận hành “trông có vẻ ổn” trên mức tải bền vững: đạt thông lượng đỉnh nhưng không còn chút lực hồi phục nào
Mầm kích hoạt (hạt bụi rơi vào nước siêu lạnh)GC pause, cold cache, một node chạy chậm, một lần reload config
Capacity dropGoodput sau sập thấp hơn throughput ngay trước sập: chi phí điều phối và tranh chấp khóa nuốt mất phần năng lực làm việc thật
Critical slowing downĐộ dao động và tính ì của độ trễ tăng dần: lực hồi phục của hệ đang cạn, và telemetry nhìn thấy điều đó trước

Vì sao vách đá là thuộc tính cấu trúc, không phải xui xẻo #

Phép suy diễn không cần giả định lạ nào — chỉ cần timeout và tài nguyên dùng chung. Bốn bước:

1. Leo dốc hàng đợi. Khi $\lambda$ tiến sát $\mu$, chiều dài hàng đợi và thời gian chờ leo theo đường hyperbola ở Mục III. Chưa có gì hỏng; đây vẫn là vùng liên tục, hệ chỉ đang chậm dần.

2. Timeout đảo chiều mọi thứ. Khi thời gian chờ vượt timeout $T_o$, phía gọi bỏ cuộc — nhưng server không biết điều đó. Nó vẫn rút các request đã hết hạn ra khỏi hàng đợi, trả đủ chi phí CPU và I/O để xử lý, rồi ghi kết quả vào những kết nối đã đóng. Đây là điểm goodput tách khỏi throughput:

$$ G \approx \lambda_{\text{nhận vào}} \cdot P(W \le T_o) $$

Đọc bằng lời: lượng việc có ích bằng lượng việc nhận vào nhân với xác suất trả lời kịp trước khi client bỏ đi. Server vẫn bận 100%, nhưng phần lớn công sức đổ vào những câu trả lời không còn ai nghe. Nhìn từ dashboard CPU, hai tình huống này giống hệt nhau.

3. Năng lực bị đốt vào việc điều phối. Tranh chấp tài nguyên biến phần năng lực còn lại thành chi phí phối hợp. Universal Scalability Law của Gunther viết tường minh số hạng này:

$$ X(N) = \frac{\gamma N}{1 + \sigma(N-1) + \kappa N(N-1)} $$

Đọc bằng lời: throughput theo số luồng song song $N$ có một số hạng ở mẫu tăng theo bình phương của $N$ — đó là $\kappa$, chi phí giữ cho mọi luồng nhìn thấy cùng một dữ liệu. Vượt một mức $N$ nhất định, số hạng đó lớn nhanh hơn tử số, và thêm luồng làm throughput giảm.

Hình ảnh cụ thể: 128 thread cùng đụng vào một bản ghi bị tranh chấp. Một thread giữ khóa trong 1 mili-giây; 127 thread còn lại quay vòng chờ, đốt trọn chu kỳ CPU để không tạo ra gì. Tốc độ xử lý hiệu dụng $\mu_{\text{eff}}$ giảm đúng vào lúc nhu cầu cao nhất.

4. Vòng lặp tự nuôi. $\mu_{\text{eff}}$ giảm kéo utilization hiệu dụng $\rho_{\text{eff}} = \lambda / \mu_{\text{eff}}$ tăng — ngay cả khi tải từ bên ngoài đứng yên. Hệ trượt xuống thung lũng thứ hai và ở lại đó: một trạng thái kẹt tự nhất quán, được nuôi bằng công việc chết và tranh chấp khóa. Không cần gì thêm để giữ nó ở đó.

Đáng chú ý là điều lập luận này không dùng tới: retry. Retry chồng thêm một vòng phản hồi dương lên cấu trúc trên và làm mọi thứ tệ hơn, nhưng động lực học của nó là chủ đề một bài riêng. Vách đá tồn tại mà không cần retry.

V. Điều kiện biên và điểm bùng phát #

Điều kiện biên #

Ba đại lượng hữu hạn giới hạn toàn bộ động lực học: sức chứa buffer $B$ (hàng đợi không nuốt được backlog vô hạn), timeout $T_o$ (công việc có hạn sử dụng), và hệ số tranh chấp $\kappa > 0$ (dữ liệu dùng chung khiến chi phí phối hợp tăng nhanh hơn tuyến tính).

Dưới ba ràng buộc này, đường goodput $G(\rho)$ không đơn trị ở gần chuyển pha. Nói bằng lời thường: với cùng một mức tải, hệ có thể đang ở một trong hai trạng thái — nhánh goodput cao thừa kế từ lúc còn thông thoáng, và nhánh goodput thấp được công việc chết giữ vững. Hệ đang nằm trên nhánh nào phụ thuộc vào lịch sử của nó, không chỉ vào mức tải hiện tại.

Đây chính là hai thung lũng ở Mục III, và nó giải thích một câu báo cáo sự cố rất quen: “tải đã về bình thường mà hệ vẫn không hồi”. Đó không phải bí ẩn, đó là hành vi bình thường của một hệ đang nằm ở thung lũng thứ hai.

Một tinh chỉnh từ vật lý giao thông. Lý thuyết ba pha của Kerner, xây từ dữ liệu cao tốc Đức, phê phán bức tranh hai trạng thái là quá sạch. Giữa “chạy thoáng” và “kẹt cứng” còn một pha thứ ba: dòng đồng bộ — xe dày, chậm hơn, tốc độ dao động mạnh, nhưng vẫn tải được khối lượng đáng kể.

Server thể hiện đúng pha trung gian đó. Node chạy 90% CPU không chết tại chỗ; nó vận hành hàng phút, hàng giờ trong vùng tranh chấp nặng, độ trễ phân tán rất rộng nhưng goodput vẫn giữ. Hệ quả thực dụng cho người làm monitoring: khoảng hai-nhánh là có thật, nhưng có một “vai đường” sống được ở giữa. Hệ cảnh báo nào coi mọi lần bước vào vai đường là sập sẽ dựng người trực dậy vì một trạng thái có thể tồn tại vô hại hàng giờ. Thứ đáng báo động là cú thoát khỏi vai đường để rơi xuống nhánh kẹt.

stateDiagram-v2 [*] --> ChayThoang : Tải thấp, xử lý ngay ChayThoang --> VaiDuong : Utilization leo vào vùng tranh chấp VaiDuong --> ChayThoang : Phân tải hoặc mở rộng năng lực kịp thời VaiDuong --> KetCung : Mầm kích hoạt (GC pause, cold cache, node chậm) KetCung --> ChayThoang : Xả tải sâu dưới sức chứa định mức, rồi nạp lại theo bậc

Hai ca bệnh trong production #

NSFNET, tháng 10/1986. Vách đá goodput đầu tiên được ghi nhận ở quy mô mạng. Router bão hòa xả bỏ gói tin; bên gửi truyền lại mà không hề chậm lại, nên một tỉ lệ ngày càng lớn của dung lượng đường truyền chở những byte chắc chắn sẽ bị xả bỏ lần nữa — đúng nghĩa đen là công việc chết đang di chuyển. Goodput rơi cỡ 1000 lần trong khi đường truyền vật lý vẫn khỏe nguyên. Congestion avoidance của Jacobson, nói bằng ngôn ngữ bài này, là một bộ điều khiển phân tán giữ $\rho$ của mọi bên gửi vĩnh viễn dưới $\rho_c$.

Roblox, tháng 10/2021. Outage 73 giờ, đọc như một thí nghiệm trình diễn về chuyện kẹt ở thung lũng sai. Mầm kích hoạt: một tính năng streaming của Consul vừa được bật dồn tranh chấp vào một Go channel duy nhất dưới tải đọc/ghi lớn, đẩy BoltDB vào một chế độ dọn dẹp bệnh lý. Độ trễ ghi p50 nhảy từ dưới 300 ms lên 2 giây — một cú nhảy, không phải một đường trôi — và mọi hệ phụ thuộc (Vault, Nomad) timeout dây chuyền.

Phần đáng học nhất nằm ở nỗ lực cứu hộ thất bại: đội kỹ sư thay cả cụm Consul bằng máy 128 core với ổ NVMe, và không ăn thua. Lý do khớp chính xác với bước 3 ở Mục IV: ràng buộc trói hệ là chi phí tranh chấp, không phải thiếu sức tính. Thêm core vào một bài toán tranh chấp khóa là làm số hạng $\kappa N(N-1)$ lớn thêm.

Hồi phục chỉ đến khi họ gỡ đúng cơ chế tạo mầm (tắt streaming) và nạp lại traffic từ 0 theo từng bậc nhỏ qua DNS. Nhánh kẹt phải được thoát xuống dưới; không mua đường thoát bằng phần cứng được.

Hai sự cố DynamoDB 2015 và Slack 2021 đã mổ xẻ ở bài trước nằm trên cùng giản đồ này: cả hai đều là hệ đã rơi sang nhánh thấp và ở lại đó.

VI. Mô phỏng định lượng #

Ba mô phỏng độc lập kiểm tra ba luận điểm. Script đầy đủ nằm trong thư mục assets của series.

Mô phỏng A — đoạn leo dốc là hyperbola, không phải đường thẳng. Tính trực tiếp thời gian chờ tại $\mu = 1000$ request/giây. Kết quả: $W$ = 2 ms tại $\rho = 0{,}5$; 5 ms tại $\rho = 0{,}8$; 20 ms tại $\rho = 0{,}95$. Đọc con số: từ 50% lên 80% tải, độ trễ tăng 3 ms. Từ 80% lên 95%, cũng ngần ấy phần trăm tải, độ trễ tăng 15 ms — gấp năm lần. Toàn bộ ngân sách độ trễ của bạn nằm trong 20% headroom cuối cùng.

M/M/1 simulation: mean waiting time diverges as utilization approaches 1

Mô phỏng B — capacity drop, và trong code không có dòng retry nào. Đây là mô hình dòng xe: một vòng đường 500 ô, xe chạy theo luật Nagel–Schreckenberg với bổ sung “khởi động chậm” của Barlović — xe đã dừng hẳn thì mất thêm thời gian để lăn bánh lại. Mỗi mật độ xe được chạy từ hai điều kiện xuất phát khác nhau.

Xuất phát từ trạng thái xe rải đều vẽ ra nhánh lưu lượng cao, sống sót tới mật độ $\rho = 0{,}16$ và đạt đỉnh $J = 0{,}79$. Xuất phát từ một khối kẹt đặc rơi về nhánh thấp, đi ngang quanh $J = 0{,}21$. Tại đúng mật độ nơi nhánh cao đạt đỉnh, nhánh kẹt tải ít hơn 73%. Giữa $\rho = 0{,}07$ và $0{,}16$, hai nhánh cùng tồn tại — đúng hai thung lũng ở Mục III.

Điểm quan trọng: không tham số nào được tinh chỉnh để tạo ra kết quả đó. Riêng sự bất đối xứng “khởi động chậm” là toàn bộ cơ chế. Trong server, bất đối xứng ấy chính là cold cache, connection pool đã cạn, và page cache đầy dữ liệu không còn dùng được.

Nagel-Schreckenberg simulation with slow-to-start rule: the fundamental diagram splits into a metastable high-flow branch and a jam outflow branch, a 73% capacity drop

Mô phỏng C — cảnh báo đến trước vách đá. Một hàng đợi chịu tải tăng chậm từ $\rho = 0{,}50$ lên $0{,}99$ qua 300.000 bước.

Phần lý thuyết, nói bằng lời trước: khi hệ tiến gần chuyển pha, khả năng tự phục hồi sau một cú xóc giảm dần. Cú xóc nào cũng lâu tan hơn cú trước. Hệ quả đo được là hai con số: độ dao động của độ trễ tăng lên, và độ trễ hiện tại ngày càng giống độ trễ vừa rồi (tự tương quan tiến về 1). Viết thành công thức:

$$ \alpha = e^{\kappa \Delta t} \to 1, \qquad \sigma^2 = \frac{\mathbb{E}[\epsilon^2]}{1-\alpha^2} \to \infty $$

Đọc bằng lời: $\kappa$ là tốc độ hệ kéo mình về trạng thái cân bằng, và nó tiến về 0 khi tới gần chuyển pha. $\alpha$ đo mức độ “ì” — giá trị bây giờ phụ thuộc bao nhiêu vào giá trị ngay trước. Khi $\alpha$ tiến về 1, mẫu số $1-\alpha^2$ tiến về 0, nên phương sai $\sigma^2$ nổ tung. Hiện tượng này gọi là critical slowing down, và nó đã được dùng làm cảnh báo sớm trong sinh thái học và khí hậu học (Scheffer et al., 2009) trước khi ai nghĩ tới việc áp vào server.

Hàng đợi tái hiện đúng dự đoán. Phương sai cuộn: 3,2 tại $\rho = 0{,}6$ → 11 tại $\rho = 0{,}75$ → 56 tại $\rho = 0{,}9$ → 707 tại $\rho = 0{,}98$. Tự tương quan: 0,87 → 0,95 → 0,985 → 0,999.

Con số đáng giá nhất nằm ở mốc $\rho = 0{,}75$: phương sai đã vượt hơn ba lần mức nền trong khi chiều dài hàng đợi trung bình vẫn trông phẳng lặng. Giá trị trung bình chỉ trở nên đáng báo động sau $\rho \approx 0{,}95$ — rất lâu sau khi cả hai thống kê kia đã rời khỏi vùng an toàn. Đó là khoảng thời gian bạn được tặng, nếu monitoring nhìn đúng chỗ.

Critical slowing down simulation: rolling variance and lag-1 autocorrelation of queue length rise well before mean latency explodes

VII. Hệ quả kiến trúc và những đánh đổi #

Một phản biện chính đáng cần trả lời trước: lý thuyết hàng đợi cộng capacity planning là một ngành đã trưởng thành — mang vật lý vào thì được thêm gì?

Ba thứ mà hàng đợi cổ điển không cung cấp: một cơ chế giải thích phần thâm hụt goodput sau bão hòa (mô hình hàng đợi tĩnh không có tham số nào cho capacity drop), một chỉ báo dẫn đường (phương sai và tự tương quan tăng trước, trong khi độ trễ trung bình là chỉ báo trễ), và ranh giới giữa gián đoạn và liên tục — thứ tách hệ suy giảm có tiếng động khỏi hệ đóng băng không báo trước.

Ở đâu ba thứ đó không cộng thêm gì — service không giữ trạng thái, timeout rộng rãi, không có dữ liệu dùng chung — thì capacity planning cổ điển là đủ, và nên bỏ bộ từ vựng nặng hơn xuống.

  1. Định nghĩa sức chứa tại điểm gãy, không phải đỉnh benchmark. Con số cực đại đo được trong load-test chính là đỉnh của nhánh siêu bền — điểm vận hành mong manh nhất mà hệ có. Hoạch định theo “90% của peak” là xếp lịch cho cả fleet đậu ngay trên đó. Hãy neo sức chứa tại điểm gãy của đường cong độ trễ, nơi phương sai bắt đầu tăng — trong các mô phỏng ở đây là $\rho \approx 0{,}7$ đến $0{,}75$. Mọi thứ phía trên điểm gãy là thời gian vay mượn, và nên được ghi vào kế hoạch đúng như vậy.
  2. Chĩa cảnh báo vào độ dao động, không chỉ vào giá trị trung bình. Thêm phương sai cuộn và tự tương quan của độ trễ (hoặc độ sâu hàng đợi) trên cửa sổ ngắn vào rổ cảnh báo, và hiệu chỉnh theo mức nền của chính service đó. Lưu ý quan trọng: vai đường ở Mục V tạo ra độ phân tán cao mà vẫn sống được, nên tín hiệu là xu hướng tiến về 1,0 và độ dốc của phương sai, không phải một mức tuyệt đối nào. DAGOR của WeChat là bằng chứng production cho cách làm này: admission control bám vào thời gian chờ trung bình trong hàng đợi với ngưỡng 20 ms — chính là điểm gãy — so với timeout 500 ms, và xả ngay 50% request ưu tiên thấp nhất tại entry service khi ngưỡng bị vượt. Dưới tải lễ Tết gấp 10 lần, cách này giữ tỉ lệ thành công cao gấp 1,5 đến 2 lần so với không có overload control, trên 3.000 microservice.
  3. Đã kẹt thì xả sâu, rồi nạp lại theo bậc. Trên nhánh kẹt, gọt tải 10-20% chỉ giữ hệ đúng chỗ mà sàn capacity drop đang giữ nó — bạn giảm tải, nhưng năng lực xử lý cũng đã tụt xuống theo. Muốn thoát phải cắt tải nhận vào xuống sâu dưới sức chứa định mức (60-80%, hoặc về 0 như Roblox đã làm), giữ đủ lâu cho hàng đợi cạn và tranh chấp tan, rồi nạp lại từng bậc. Đây là một kill switch có sẵn kèm runbook đã diễn tập, không phải thứ ứng biến giữa lúc sự cố.
Biện phápĐược gìTrả giá gì
Hoạch định theo điểm gãyFleet không bao giờ đậu trên nhánh siêu bền; spike rơi vào vai đường thay vì rơi khỏi váchNhiều phần cứng hơn trên mỗi đơn vị traffic; phải đo lại điểm gãy khi đặc tính workload thay đổi
Cảnh báo theo phương saiĐược thêm hàng phút nhờ chỉ báo dẫn đường; chuông kêu trước khi giá trị trung bình dịch chuyểnTốn công đo mức nền cho từng service; đặt ngưỡng tuyệt đối sẽ báo động nhầm mỗi lần hệ bước vào vai đường
Kill switch xả sâuLối thoát đáng tin duy nhất khỏi nhánh kẹt; biến cái bẫy 73 giờ thành một chu trình xả-và-hồiChủ động từ chối phần lớn traffic thật; đòi hỏi phân lớp ưu tiên chính xác và một lộ trình nạp lại đã kiểm thử

VIII. Tài liệu tham khảo #