結論先講
壓測數據的視覺化只需要 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 分析會拆成:
| 類型 | 代表什麼 |
|---|---|
| 4xx | Client 端問題(重複 email、驗證失敗) |
| 5xx | Server 端崩潰(uncaught exception、OOM) |
| timeout | Server 來不及回應 |
| refused | Server 拒絕連線(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/VU | 500 VU 時 RPS/VU | 下降倍數 |
|---|---|---|---|
| Go | 2.2 | 0.15 | 15x |
| Express-TS | 2.4 | 0.11 | 22x |
| Spring Boot | 1.5 | 0.03 | 50x |
| Django | 1.7 | 0.002 | 850x |
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
- 先看 VU vs RPS: 找到 RPS 開始下降或停止成長的 VU 等級 → 這是 breaking point
- 看 Response Time: 確認 P95 在 breaking point 前後的變化 → 量化「慢了多少」
- 看 Error Rate: 確認錯誤什麼時候開始出現,搭配 RPS 排除假象
- 看擴展效率: 確認 RPS/VU 跌破 0.1 的 VU 等級 → 這是「服務堵塞」的精確臨界點
- 需要時才看其他圖: Network Timing(定位是連線問題還是處理問題)、Status 分佈(定位錯誤類型)
下一篇
Bcrypt 是萬惡之源 — 看完 Grafana 你會發現一個現象:不管哪個框架,Response Time 在某個 VU 之後都急劇上升。原因都一樣——bcrypt。下一篇把這個「共同公敵」徹底拆解。
本系列文章
完整 68 篇目錄見 系列首頁
← 上一篇:k6 腳本怎麼寫:從 Hello World 到混合場景 → 下一篇:Bcrypt 是萬惡之源:密碼 Hash 為什麼這麼慢