什麼訊號代表該擴容、什麼代表可以縮容,規模拐點判斷的根本概念是飽和曲線:系統不是「撐得住」跟「掛掉」的二元,中間有一段可測量的劣化區。負載往上加,系統先在延遲平穩的線性區,過了某個點進入延遲開始上升的膝點區(knee),再往上就是延遲不可預測的懸崖區(cliff)。拐點判斷的核心,是在系統進入膝點區、還沒到懸崖時就讀出訊號,而不是等它掉下懸崖。

判準先建立在利用率的三段區間上。利用率低於 50% 是線性區,延遲平穩;50% 到 80% 是膝點區,延遲開始上升但仍可接受;超過 80% 是懸崖區,延遲不可預測、可能逾時或引發級聯故障。健康的系統應該運轉在 50% 到 70% 之間,這也是 峰值估算 裡安全係數要覆蓋的那段 headroom。

這三段區間背後是排隊理論:利用率往高處走,佇列長度增加的幅度越來越陡——50% 到 70% 增加數倍、70% 到 90% 再急遽放大(實際倍數視佇列模型而定,但方向一致是超線性);延遲跟佇列長度成正比,所以延遲在高利用率區是指數成長。80% 這個懸崖門檻是通用的形狀,膝點的實際位置隨系統類型變——無狀態服務的膝點大約在 80% CPU、有鎖競爭的資料庫大約 60%、吃磁碟 I/O 的佇列服務可能 50% 就到。這就是為什麼安全係數不能對所有系統套同一個數:有狀態的關鍵系統膝點來得早,要留的 headroom 比無狀態服務厚。判斷一個系統該在多少利用率就準備擴容,要看它是哪一類、膝點在哪,不是一律套 80%。

膝點的早期訊號:p99 先於平均劣化

膝點的價值在於它是「該擴容」的早期訊號,但它只在對的指標上看得見。四個訊號共同指向系統正在進入膝點區:吞吐量從線性成長轉為次線性(加壓上去、吞吐不再等比增加)、p50 還穩但 p99 與 p999 開始飆、佇列開始堆積(不只是利用率上升)、而錯誤率仍接近零。最後這條很關鍵——膝點區的系統還沒開始報錯,只看錯誤率會以為一切正常,要看尾延遲才抓得到。

尾延遲的敏感度是這裡的判讀核心。p50 對垃圾回收暫停、重試風暴、長尾都不敏感,純看平均或 p50 會誤判「飽和還沒到」;p99 對連線競爭敏感,能較早看到飽和的苗頭;p999 對垃圾回收的 stop-the-world、leader 選舉、重試風暴最敏感。看容量該不該擴,看的是 p99 跟 p999 的曲線有沒有開始上翹,不是平均值。等到吞吐量開始下降、p99 變成逾時、錯誤率飆升、重試風暴出現,那已經是掉進懸崖區了,擴容是在救火而不是預防。

用 ramp-up 找膝點,不用單點壓測

膝點的位置要靠階梯加壓(ramp-up)找,不能用固定的單一壓力值測。按 1 倍、2 倍、4 倍、8 倍逐級加壓,每一級維持 5 到 10 分鐘看穩態行為,才能看出曲線在哪個負載開始上翹。一個「2000 RPS 撐在 100 毫秒」的單點結果毫無定位價值——它可能離膝點還很遠、也可能已經在懸崖邊,單點測不出來。用哪個工具做這種階梯加壓、怎麼讀每一級的輸出,見 壓力測試工具與方法;這裡要確立的是拐點判讀本身:拐點是一條曲線上的位置,要用曲線去找,不是一個點。

找出哪個資源先飽和

系統的容量由最先飽和的那個資源決定,所以每次 ramp-up 要同時盯多個維度,看哪個先到頂:

  • CPU:看 load average 與 run queue,不只看利用率——利用率 100% 但 run queue 是空的仍撐得住,run queue 開始堆才是真的 CPU 飽和。
  • 記憶體:垃圾回收暫停、swap、快取驅逐都是隱性的記憶體飽和,不會反映在單純的用量數字上。
  • 磁碟 I/O:吞吐量、IOPS、佇列深度三個維度分開看——雲端 SSD 通常先撞 IOPS 上限,本機 NVMe 先撞吞吐量上限。
  • 網路:頻寬、每秒封包數(PPS)、連線數。雲端的 PPS 上限超過時可能是靜默丟包,不報錯。
  • 連線池:最常見的隱性瓶頸。一個大小 100 的連線池用到 95 就已經接近飽和,但 CPU、記憶體都還很閒——瓶頸不在機器、在池子。
  • 外部 API 配額:看對方回的 429 錯誤率,這個上限不在自己手上。
  • 檔案描述元與連接埠:ephemeral port 與 file descriptor 是 OS 層的配額,跟應用的連線池是不同層——連線池還沒滿、但 OS 的 fd 或可用 port 先耗盡,一樣接不了新連線,且症狀(Too many open files、connect 失敗)容易被誤判成應用問題。

還有一種隱性飽和不在這六維裡:熱分區(hot partition)。分散式的鍵值或資料庫,名義容量是每個分區上限乘以分區數,但鍵分布不均時,整體利用率可能只有 20%、最熱的那個分區已經 100%,再加流量立刻被限流。訊號是「吞吐量上不去、但平均利用率很低」加上「某些鍵的延遲飆升、出現限流事件」。處理靠複合鍵、寫入分片或用快取吸收熱鍵——這種飽和用整體利用率完全看不到。

該擴容之後:往垂直還是水平

確認要擴之後,下一個判斷是往哪個軸擴。垂直擴展(換更大的機器)不必改程式,但受單機物理上限限制、成本非線性(高階機型有溢價)、且是單點;水平擴展(加更多台)理論上線性,但要求服務無狀態或狀態能同步,且每台都要付基礎成本。經驗法則是無狀態的部分水平擴、有狀態的關鍵節點(如資料庫主節點)垂直擴加讀取副本,兩者常同時進行。

垂直擴展有兩道牆要知道。第一道是物理上限,但真正的天花板常常比規格更早到——雲端有記憶體達 TiB 級的超大機型,但有狀態的交易型資料庫主節點,真實天花板常卡在 32 到 64 vCPU,因為鎖競爭、context switch、記憶體頻寬這些架構因素,不是規格不夠。第二道是成本曲線:中階機型從 4 到 8 vCPU、8 到 16 vCPU 大約是 1.8 到 1.9 倍(接近線性),但過了 48 vCPU 明顯偏離線性、高階機型溢價更陡。垂直擴到一個點之後,每多一分效能付的錢急遽變貴,那就是該轉水平或該分片的訊號。

水平擴展的隱性成本則在協調與連線放大。加到很多台後會引入協調成本(共識、分散式鎖帶來新的故障模式與延遲),以及連線池放大——100 台每台開 10 條連線,資料庫就要扛 1000 條連線,機器數線性成長、資料庫連線數也跟著線性成長,最後瓶頸從應用層移到資料庫的連線上限。這條在 模組二 水平擴展 展開;關鍵是水平擴展把成本換到協調與下游連線上,不是免費的線性擴張。

擴縮訊號的判讀

擴縮的自動化要選對訊號、設對門檻。常見的擴容訊號各有適用與失準:CPU 通用但對 I/O bound 的服務失準、佇列深度快且直接反映積壓、p95 延遲是用戶體驗訊號但等延遲出現才擴可能來不及。設定順序是先選訊號、再設門檻、再加冷卻時間(cool-down)防止反覆抖動。

幾個擴縮後的訊號直接指出問題出在哪:加了機器但 QPS 沒提升,代表有狀態殘留(流量沒被新機器分攤);加了機器但資料庫連線爆掉,是連線池放大;自動擴縮反覆擴了又縮,是冷卻時間太短或訊號在抖動;尖峰時新機器來不及起來,是冷啟動太慢或預測不夠提前。這些訊號讀對了,才知道該調的是訊號、門檻、冷卻時間,還是根本不該用自動擴縮(可預期的尖峰要用排程式或預測式擴容,不是等訊號觸發)。

下一步路由