Bài 05: Tự tổ chức tới hạn — tại sao hệ thống luôn trôi về bờ vực sụp đổ
Mục lục

I. Tóm tắt #
Dashboard báo CPU 80%, error rate 0%. Trực giác tuyến tính kết luận: còn 20% dự phòng, hệ thống an toàn. Mô hình đống cát Bak-Tang-Wiesenfeld và lý thuyết Highly Optimized Tolerance nói ngược lại: hệ phân tán không đứng yên trong vùng an toàn — nó tự trôi về điểm tới hạn, và áp lực tối ưu chi phí chính là động cơ đẩy. Ở đó, kích thước sự cố tuân theo power law: cùng một trigger nhỏ, hôm nay vô hại, ngày mai lan thành sạt lở khắp cụm. Điểm nghịch lý: hệ thống nằm ở mép vực không phải vì một quy luật tự nhiên nào ép buộc, mà vì chính bạn — qua từng sprint cắt giảm chi phí và nâng utilization — đã đẩy nó tới đó. Bài này trình bày phần toán của sự trôi dạt, và cách giữ hệ thống lùi lại khỏi mép.
II. Vấn đề: nghĩ theo đường thẳng trong một hệ không thẳng #
Ngụy biện tuyến tính phát biểu thế này: hệ thống có dung lượng $k_{max}$, tải hiện tại $k(t)$; chừng nào $k(t) < k_{max}$ thì mọi thứ trong tầm kiểm soát. Muốn an toàn hơn? Thêm retry, thêm cache, thêm autoscaling. Mỗi lớp bảo vệ là thêm một lớp an toàn.
Jens Rasmussen (1997) phá vỡ bức tranh đó bằng một hình vẽ đơn giản. Mọi hệ thống có con người vận hành đều di chuyển trong một vùng bị chặn bởi ba đường biên: biên phá sản (làm chậm và đắt quá thì công ty chết), biên quá tải nhân lực (bắt người làm nhiều quá thì đội ngũ gãy), và biên an toàn (đi quá thì hệ thống sập).
Áp lực kinh doanh — nhanh hơn, rẻ hơn — là một lực đẩy liên tục, và nó đẩy điểm vận hành ra xa hai biên đầu. Hình học của chuyện này chỉ có một kết cục: điểm vận hành bị ép về phía biên thứ ba. Không ai ra quyết định “hãy tiến gần biên an toàn hơn”; nó là hệ quả của việc liên tục tránh hai biên kia.
Sidney Dekker (2011) gọi quá trình đó là drift into failure — trôi dần vào sự cố — kèm một nhận xét khó chịu: sự trôi dạt không đến từ cẩu thả, nó đến từ thành công. Mỗi lần nén container chặt hơn, bỏ một bước kiểm tra để tiết kiệm vài mili-giây, thêm retry để che một lỗi mạng chập chờn — hệ thống chạy tốt hơn, rẻ hơn, và tiêu bớt một phần dự phòng không hiện trên bất kỳ dashboard nào.
Dị thường xuất hiện đúng lúc không ai chờ: một độ trễ mạng vài giây, một đợt cache lạnh sau kỳ nghỉ, một file cấu hình phình to. Cùng một loại trigger: lần này chỉ gợn nhẹ, lần sau kéo dài sự cố nhiều giờ. Kích thước hậu quả không tỉ lệ với kích thước nguyên nhân — và đó chính là chữ ký của một hệ đang nằm ở trạng thái tới hạn.
III. Khung lý thuyết #
Đống cát của Bak, Tang và Wiesenfeld #
Bak, Tang và Wiesenfeld (1987) mô tả hiện tượng này bằng một trò chơi cực đơn giản. Lấy một lưới ô vuông, thả từng hạt cát xuống các ô ngẫu nhiên. Gọi $z(x,y)$ là số hạt đang nằm ở ô $(x,y)$. Khi một ô đủ bốn hạt ($z \ge z_c = 4$), nó sụp: mất 4 hạt, chia đều cho 4 ô bên cạnh. Ô bên cạnh nhận thêm cát có thể vượt ngưỡng theo, tạo phản ứng dây chuyền — một trận sạt lở — cho đến khi cả lưới ổn định.
Điểm đáng chú ý không nằm ở quy tắc, mà ở trạng thái mà đống cát tự tiến hóa đến. Không ai chỉnh tham số nào cả, nhưng đống cát luôn tự dốc dần lên tới một độ dốc nhất định rồi dừng ở đó. Vật lý gọi trạng thái đó là attractor — cái đích mà hệ tự tìm về bất kể xuất phát từ đâu. Đây là chữ “tự tổ chức” trong Self-Organized Criticality (SOC): không ai đặt hệ vào trạng thái tới hạn, nó tự đi tới.
Ở trạng thái đó, kích thước các trận sạt lở tuân theo power law:
$$ P(s) \sim s^{-\tau} $$Đọc bằng lời: sạt lở nhỏ thì rất nhiều, sạt lở lớn thì hiếm nhưng không bao giờ hiếm tới mức bỏ qua được. $P(s)$ là mật độ xác suất của kích thước sạt lở $s$; $\tau$ là độ dốc của đường thẳng khi vẽ trên trục log-log.
Hệ quả thực dụng của công thức đó nằm ở chỗ nó không có kích thước điển hình. Cùng một hạt cát, rơi hôm nay xáo trộn 2 ô, rơi ngày mai kích hoạt trận sạt lở quét cả lưới. Câu hỏi “một sự cố điển hình lớn cỡ nào” không có câu trả lời — cũng như câu hỏi “một outage điển hình kéo dài bao lâu” không có, và đó là lý do mọi kế hoạch dung lượng dựa trên sự cố trung bình đều hụt.
Hai điều kiện duy trì trạng thái này:
- Nạp chậm, xả nhanh. Cát rơi từng hạt (chậm), sạt lở xảy ra gần như tức thời (nhanh). Nhờ vậy các trận sạt lở không chồng lên nhau, và hệ kịp dọn về độ dốc tới hạn sau mỗi lần.
- Hệ số phân nhánh $\sigma = 1$. $\sigma$ là số ô sụp mới mà trung bình một ô sụp kéo theo. $\sigma < 1$: sự cố tắt dần theo cấp số nhân — một ô sụp kéo 0,8 ô, rồi 0,64, rồi tắt. $\sigma > 1$: bùng nổ — 1,2 rồi 1,44 rồi không dừng. Trạng thái tới hạn ghim hệ đúng tại $\sigma = 1$, điểm chông chênh nhất, nơi một dao động nhỏ có thể lan không giới hạn.
Cần một lưu ý trước khi đi tiếp: Roman Frigg (2003) đã cảnh báo xu hướng dán nhãn SOC lên mọi hiện tượng có power law. Đống cát là hệ tự nhiên, không ai thiết kế nó. Còn data center thì có kiến trúc sư. Câu hỏi đó dẫn thẳng đến khung lý thuyết thứ hai.
HOT: tới hạn không phải tai nạn, nó là sản phẩm thiết kế #
Carlson và Doyle (1999) đề xuất Highly Optimized Tolerance (HOT) như đối trọng của SOC. Luận điểm: trong các hệ do con người thiết kế, power law không sinh ra từ sự tự tổ chức mù quáng, mà là hệ quả trực tiếp của một bài toán tối ưu có ràng buộc.
Bài toán đó rất quen: tối đa hóa hiệu quả trong giới hạn chi phí và tài nguyên, đồng thời phòng thủ trước danh sách rủi ro đã biết. Lời giải của nó luôn hội tụ về một hình thái mang tính cách kép, gọi là robust-yet-fragile:
- Vững chãi trước những nhiễu loạn đã được tính tới — nhờ buffer, retry, replica, cầu dao.
- Cực kỳ mong manh trước bất kỳ nhiễu loạn nào nằm ngoài danh sách đó.
Cơ chế đứng sau tính cách kép này đáng dừng lại một chút: mọi nỗ lực chống lỗi thường ngày đều nén rủi ro cục bộ lại và dồn nó vào phần đuôi phân phối. Retry làm lỗi mạng lẻ tẻ biến mất khỏi thống kê, đổi lại nó tạo ra khả năng một cơn bão retry. Autoscaling làm các spike nhỏ vô hình, đổi lại nó tạo ra khả năng một hóa đơn khổng lồ hoặc một đợt scale-out kéo sập database. Bạn không loại bỏ rủi ro, bạn đổi nhiều sự cố nhỏ lấy ít sự cố khổng lồ.
Đây là điểm mấu chốt của bài: hệ thống của bạn không trôi về điểm tới hạn vì một quy luật tự nhiên nào ép buộc. Nó ở đó vì chính bạn — qua từng sprint tối ưu chi phí cloud và nâng utilization — đã đẩy nó đến. SOC mô tả động lực học của sự trôi dạt; HOT chỉ ra nguyên nhân.
IV. Quy đổi sang kiến trúc phần mềm #
| Biến số trong mô hình | Tương ứng trong hệ thống thật |
|---|---|
| Hạt cát | Request từ client, hoặc mỗi lần deploy — năng lượng nạp vào chậm và liên tục |
| Độ dốc tới hạn ($z_c$) | Ngưỡng utilization $\rho_c$: trần CPU, thread pool, connection pool. Vượt qua thì độ trễ chạy ra vô cùng |
| Trận sạt lở | Cascading failure: một node quá tải đẩy phần việc dở sang các node còn lại, lần lượt kéo chúng vượt ngưỡng |
| Tia sét (trigger) | Chập chờn mạng, cấu hình sai, cache lạnh, một đợt tăng tải đột biến |
| Hệ số phân nhánh ($\sigma$) | Retry amplification: một request lỗi sinh ra $r$ request mới. Cấu hình retry 3 lần đẩy $\sigma$ tức thời vượt 1 |
| Mật độ rừng | Nợ kỹ thuật và việc che lỗi: mỗi lỗi nhỏ bị nuốt im lặng là một cụm cây chưa cháy — nhiên liệu tích tụ cho đám cháy lớn |
Hàng cuối bảng cần nói thêm. Mô hình cháy rừng của Drossel và Schwabl (1992) thêm chiều không gian cho SOC: cây mọc với xác suất $p$, sét đánh với xác suất $f$, sét trúng cây thì cả cụm cây liền kề cháy tức thời.
Chiếu sang phần mềm: việc dập mọi lỗi nhỏ — pod tự restart quanh một memory leak, exception bị nuốt im lặng, retry vô hạn phủ lên lỗi mạng — chính là lực lượng kiểm lâm dập từng đám cháy con. Rừng ken đặc dần. Nợ kỹ thuật nối các cụm nhỏ thành một khối liền nhau, và đến khi có một tia sét đủ lớn thì lửa lan không còn ranh giới nào để dừng. Mục VII sẽ quay lại ý này với chaos engineering.
V. Điều kiện biên và điểm bùng phát #
Điều kiện biên, dịch sang ngôn ngữ vận hành #
- Nạp chậm, xả nhanh. Tải nạp vào từ từ (request, deploy), năng lượng xả ra gần như tức thời (một cascade). Chừng nào thời gian phục hồi còn ngắn hơn nhiều so với nhịp nạp tải, các cascade không chồng lên nhau và hệ tự dọn về độ dốc tới hạn sau mỗi lần. Điều kiện này gãy khi sự cố xảy ra dày hơn tốc độ đội ngũ khắc phục — lúc đó hệ không còn quay về được trạng thái nền.
- Hệ số phân nhánh quanh $\sigma = 1$. Mỗi node quá tải kéo theo trung bình $\sigma$ node quá tải mới. Nâng utilization để tiết kiệm chi phí chính là đẩy $\sigma$ tiến về 1 — vì càng ít chỗ trống, một node chết càng dễ đẩy node bên cạnh vượt ngưỡng.
Điểm bùng phát #
Gọi $\rho$ là utilization và $\rho_c$ là ngưỡng tới hạn — nơi độ trễ hàng đợi bắt đầu chạy ra vô cùng.
Khi $\rho$ còn xa $\rho_c$, kích thước cascade có một mức chặn hữu hạn: sạt lở lớn nhất bị dập lại nhanh, vì luôn có node còn chỗ trống để hứng phần việc dở. Khi $\rho$ tiến sát $\rho_c$, mức chặn đó lớn dần. Ở lân cận $\sigma = 1$, không còn mức chặn nào — một cascade đơn lẻ có thể quét toàn cụm.
Đây là ý nghĩa của “điểm bùng phát” trong bài này, và nó khác với cách hiểu thông thường: không phải một ngưỡng dung lượng cứng bị vượt qua, mà là phần đuôi của phân phối dày lên đến mức sự kiện toàn hệ trở thành chuyện thường kỳ. Trước và sau điểm đó, dashboard trông giống hệt nhau. Cái đổi là phân phối kích thước sự cố, thứ không có widget nào hiển thị.
Cơ chế đẩy $\sigma$ vượt 1 rõ nhất trong hệ phân tán là retry storm: request timeout sinh thêm request, tải hiệu dụng tự nuôi chính nó ngay cả khi trigger đã biến mất. Đó là dạng metastable failure mà Bronson và cộng sự (HotOS 2021) rồi Huang và cộng sự (OSDI 2022) ghi nhận trên hàng trăm postmortem của AWS, Google và Cloudflare — sự cố DynamoDB 2015 hay Slack tháng 1/2021 đều cùng một khuôn. Vì sao hệ kẹt lại sau khi trigger đã hết là chủ đề của bài kế tiếp. Ở đây chỉ cần một kết luận: muốn hệ ổn định, phải đưa $\sigma$ xuống dưới 1 và chặn bán kính của mỗi cascade.
VI. Mô phỏng định lượng #
Đoạn mô phỏng dưới đây dựng lại đúng trò chơi ở Mục III: lưới $64 \times 64$, thả 60.000 hạt cát, đo kích thước từng trận sạt lở.
| |
Kết quả: phân phối kích thước sạt lở hội tụ về một đường thẳng trên trục log-log với độ dốc $\tau \approx 1{,}1$ — nằm trong khoảng các giá trị được báo cáo cho lưới hai chiều, vốn dao động tùy cách chia thùng và cách đếm.
Hai chi tiết đáng đọc kỹ trong biểu đồ. Thứ nhất, phần đuôi gãy xuống ở cuối: đó là hiệu ứng kích thước hữu hạn — trận sạt lở lớn nhất bị chặn bởi biên lưới, đúng như bán kính ảnh hưởng của một sự cố bị chặn bởi kích thước cụm. Thứ hai, và quan trọng hơn: không tham số nào được tinh chỉnh để tạo ra đường thẳng này. Không ai đặt lưới vào trạng thái tới hạn; nó tự đi tới sau vài nghìn hạt cát đầu tiên. Đó chính là phần “tự tổ chức” của SOC, và cũng là lý do không có cấu hình nào của hệ thống bạn có thể “tắt” hiện tượng này.

VII. Hệ quả kiến trúc và những đánh đổi #
Nếu áp lực kinh tế là hằng số và HOT là đúng, kiến trúc sư không ngăn được hệ thống tiến về biên tới hạn. Việc làm được là giữ khoảng cách tới ngưỡng, và chặn bán kính mỗi cascade. (Các đòn đánh trực tiếp vào vòng lặp retry — retry budget, adaptive concurrency — thuộc bài kế tiếp.)
- Chừa chỗ trống — đừng vận hành ở đúng độ dốc tới hạn. Phần utilization bỏ trống không phải lãng phí, nó là khoảng đệm hấp thụ trigger. Giữ $\rho$ đủ xa $\rho_c$ — nhiều nhóm nhắm $\rho \le 0{,}6$ cho dịch vụ nhạy độ trễ — để một spike không đẩy hệ qua $\sigma = 1$. Cần nói thẳng: đây là quyết định ở tầng ngân sách chứ không phải tầng code, vì nó đi ngược đúng cái lực đã đẩy hệ tới mép vực ở Mục II.
- Kiểm soát đầu vào — giới hạn tốc độ nạp cát. Khi hàng đợi bắt đầu dồn, nhận thêm việc chỉ đẩy hệ sâu hơn vào vùng tới hạn. Từ chối sớm — trả 503 cho lưu lượng không thiết yếu — giữ phần lớn lưu lượng sống và chặn cascade từ gốc. Đánh đổi: bạn chủ động bỏ một phần request thật, nên phân loại ưu tiên phải chính xác.
- Chaos engineering như đốt rừng có kiểm soát. Nhìn từ mô hình cháy rừng ở Mục IV, dập sạch mọi lỗi nhỏ chỉ tích nhiên liệu cho đám cháy lớn. Chủ động tiêm lỗi — tắt node ngẫu nhiên, thêm độ trễ giả — là đốt trước những cụm liên kết mong manh ở quy mô còn kiểm soát được, đồng thời buộc các đường phục hồi phải chạy thật trước khi khủng hoảng thật xảy ra.
| Biện pháp | Được gì | Trả giá gì |
|---|---|---|
| Chừa chỗ trống (ρ ≤ 0,6) | Đệm hấp thụ trigger; giữ hệ ở dưới mức tới hạn thay vì đúng tại đó | Chi phí hạ tầng tăng thẳng; đi ngược áp lực kinh tế nên cần cam kết từ tầng ngân sách |
| Kiểm soát đầu vào | Giữ được phần lớn lưu lượng có ích; chặn cascade từ gốc | Chủ động bỏ một phần request thật; cần phân loại ưu tiên lưu lượng chính xác |
| Chaos engineering | Xả năng lượng bằng nhiều cascade nhỏ có kiểm soát; lộ đường phục hồi yếu sớm | Rủi ro vận hành khi tiêm lỗi; đòi hỏi khả năng quan sát tốt và văn hóa chấp nhận thất bại |
VIII. Tài liệu tham khảo #
- Bak, P., Tang, C., & Wiesenfeld, K. (1987). Self-organized criticality: An explanation of the 1/f noise.
- Bronson, N., Aghayev, A., Charapko, A., & Zhu, T. (2021). Metastable Failures in Distributed Systems.
- Carlson, J. M., & Doyle, J. (1999). Highly optimized tolerance: A mechanism for power laws in designed systems.
- Dekker, S. (2011). Drift into Failure: From Hunting Broken Components to Understanding Complex Systems. CRC Press.
- Dhar, D. (1990). Self-organized critical state of sandpile automaton models.
- Drossel, B., & Schwabl, F. (1992). Self-organized critical forest-fire model.
- Frigg, R. (2003). Self-organised criticality — what it is and what it isn’t.
- Huang, L., Magnusson, M., Muralikrishna, A. B., et al. (2022). Metastable Failures in the Wild.
- Rasmussen, J. (1997). Risk management in a dynamic society: A modelling problem.