結論先講

壓測數據的視覺化只需要 4 張圖。 按照這個順序看:(1) VU vs RPS 看吞吐量有沒有跟上 (2) Response Time 分位數看延遲分佈 (3) Error Rate 看什麼時候開始壞 (4) 擴展效率看每加一個 VU 值不值得。其他圖表都是「確認細節」用的,不是第一眼要看的。


圖表一:VU vs RPS — 最重要的一張圖

這張圖左軸是 VU(虛擬用戶數),右軸是 RPS(每秒請求數)。它回答一個最基本的問題:加更多用戶,系統能處理更多請求嗎?

健康的樣子

VU:  ──────────────/──────────────
RPS: ──────────/──────────────────
     VU 上升時 RPS 跟著上升,然後 RPS 平穩 = 到達天花板

VU 從 10 爬到 100,RPS 也從 20 爬到 70。這代表系統在 scale up——加人就能處理更多請求。

開始出問題的樣子

VU:  ──────────────────/──────────
RPS: ──────────/──────\───────────
     RPS 開始下降但 VU 還在上升 = 系統過載

VU 從 100 繼續爬到 500,但 RPS 從 70 掉到 40。更多用戶反而讓系統變慢了——這就是 breaking point。

完全崩潰的樣子

VU:  ──────────────────────/──────
RPS: ─────────\───────────────────
     RPS 趴在地上,VU 還在衝 = 服務堵塞

5,000 VU 但 RPS 只有 4——代表 99.9% 的 VU 連 request 都送不出去。

真實案例:Go 在 CRUD 場景

  • 10 VU → 22 RPS(每個 VU 貢獻 2.2 個請求/秒)
  • 50 VU → 64 RPS(還在成長)
  • 100 VU → 69 RPS(成長趨緩)
  • 500 VU → 74 RPS(天花板)
  • 1K VU → 33 RPS(開始下降 = 過載)
  • 5K VU → 4 RPS(完全崩潰)

拐點在 100-500 VU 之間。 這就是這張圖最重要的資訊。


圖表二:Response Time 分位數

這張圖畫 4 條線:P50(中位數)、P90、P95、P99。

為什麼不看平均值

平均值會被極端值拉高。想像 100 個 request,99 個 50ms,1 個 5,000ms。平均是 99ms——「看起來還好」。但 P99 是 5,000ms——「有 1% 的用戶等了 5 秒」。

怎麼讀這張圖

  • 四條線貼在一起: 延遲分佈很均勻,系統健康
  • P99 突然噴開: Tail latency 問題,可能是 GC 暫停或某個查詢特別慢
  • 四條線一起飆升: 整體都變慢了,CPU 或 DB 到達極限
  • P95 超過 3 秒: 規則引擎會標記為 Critical

真實案例:.NET Core

  • 10 VU:P50=150ms, P95=726ms(四條線還算近)
  • 50 VU:P50=1s, P95=6s(開始拉開)
  • 100 VU:P50=3s, P95=12 秒(P95 是 P50 的 4 倍 = 嚴重不穩定)

P95 12 秒代表每 20 個用戶就有 1 個人等超過 12 秒。這在生產環境是不可接受的。


圖表三:Error Rate

看起來最簡單但最容易誤讀的圖。

0% Error ≠ 沒問題

這是我們在壓測中發現的最重要的 insight:服務堵塞時,error rate 可能是 0%

為什麼?因為堵塞不是「回錯誤」,是「根本沒回」。k6 的 VU 送出 request 後一直等、等到 timeout。timeout 的 request 在某些配置下不算 error——它們算 timeout,是另一個指標。

所以看 Error Rate 的時候,必須同時看 RPS:

  • Error 0% + RPS 正常 = 真的沒問題
  • Error 0% + RPS 很低 = 假象,服務堵塞了
  • Error 飆升 + RPS 正常 = 服務在處理,但處理結果是錯的
  • Error 飆升 + RPS 飆升 = Nginx/reverse proxy 在快速拒絕連線(如 Laravel 10K VU 的情況)

Error 的類型也很重要

我們平台的 Error 分析會拆成:

類型代表什麼
4xxClient 端問題(重複 email、驗證失敗)
5xxServer 端崩潰(uncaught exception、OOM)
timeoutServer 來不及回應
refusedServer 拒絕連線(connection limit)

FastAPI 在高 VU 時出 5xx(uvicorn worker 崩潰),Express-JS 出 refused(連線數到頂),Laravel 出 refused(Nginx connection limit)。同樣是「錯誤」,原因和修法完全不同。


圖表四:擴展效率(RPS/VU)

這張圖畫的是「每加一個 VU 能帶來多少 RPS」。它是前面三張圖的「導數」——把趨勢量化成一個數字。

怎麼讀

  • RPS/VU > 1: 加一個 VU 能多處理超過 1 個 request/秒。效率好
  • RPS/VU = 0.1: 加一個 VU 只多處理 0.1 個 request/秒。已經在浪費資源
  • RPS/VU < 0.01: 加 VU 已經沒有意義了。服務堵塞

真實案例

框架10 VU 時 RPS/VU500 VU 時 RPS/VU下降倍數
Go2.20.1515x
Express-TS2.40.1122x
Spring Boot1.50.0350x
Django1.70.002850x

Django 在 500 VU 時的 RPS/VU 是 0.002——每個 VU 每秒只能處理 0.002 個請求。等於一個 VU 要等 500 秒才能完成一個請求。這個數字比看「avg 34 秒」更直覺地說明了「服務已經死了」。


其他圖表(補充用)

HTTP Status 分佈

圓餅圖或堆疊長條圖,顯示 200/4xx/5xx 的比例。在 Error Rate 圖看到錯誤後,用這張圖確認「錯誤是什麼類型」。

Network Timing

拆解每個 request 的時間組成:

  • Connecting: TCP 三次握手
  • Sending: 把 request body 傳出去
  • Waiting: Server 在處理(最關鍵的部分)
  • Receiving: 把 response body 收回來

正常情況下 Waiting 佔 90% 以上。如果 Connecting 很高,代表連線數到上限了。如果 Receiving 很高,代表 response body 太大。

VU 數量

確認 VU 的 ramp-up 有沒有按照 stages 設定跑。有時候因為系統堵塞,k6 來不及建立新的 VU,實際 VU 數會比設定的少。


看圖的 SOP

  1. 先看 VU vs RPS: 找到 RPS 開始下降或停止成長的 VU 等級 → 這是 breaking point
  2. 看 Response Time: 確認 P95 在 breaking point 前後的變化 → 量化「慢了多少」
  3. 看 Error Rate: 確認錯誤什麼時候開始出現,搭配 RPS 排除假象
  4. 看擴展效率: 確認 RPS/VU 跌破 0.1 的 VU 等級 → 這是「服務堵塞」的精確臨界點
  5. 需要時才看其他圖: Network Timing(定位是連線問題還是處理問題)、Status 分佈(定位錯誤類型)

下一篇

Bcrypt 是萬惡之源 — 看完 Grafana 你會發現一個現象:不管哪個框架,Response Time 在某個 VU 之後都急劇上升。原因都一樣——bcrypt。下一篇把這個「共同公敵」徹底拆解。


本系列文章

完整 68 篇目錄見 系列首頁

← 上一篇:k6 腳本怎麼寫:從 Hello World 到混合場景 → 下一篇:Bcrypt 是萬惡之源:密碼 Hash 為什麼這麼慢