結論先講

9 個框架,同一個天花板。 在 2 核 2 GB 的 Docker 環境下,沒有任何框架能在 500 VU 以上維持健康服務。原因不是框架不夠好,而是 CRUD 流程中的 bcrypt hash 吃掉了所有 CPU 預算。Go 在低延遲上贏了(10 VU 時 avg 112ms),但到了高 VU,大家一樣趴在地上。

如果你是從第 25 篇 bcrypt 專題過來的,接下來你會看到理論和現實完全吻合。

這一步怎麼來的:搞清楚 bcrypt 是共同天花板之後,想知道「撇開 bcrypt,框架之間到底差多少」。於是從最基礎的 User CRUD 開始,9 個框架逐一比較。


測試場景

項目設定
流程Register → Login → Get Profile → Update → Delete
VU 梯度10, 50, 100, 500, 1K, 5K, 10K
每個 VU完整跑一次 CRUD 流程
控制變因2 核 / 2 GB / MySQL 8.0 / bcrypt rounds=10 / Pool Max 20

5 步操作中,Register 和 Login 會觸發 bcrypt(佔 40%)。這個比例比真實應用高很多——第 34 篇的混合場景會把 Auth 降到 10%,結果完全不同。


排名總覽

#框架健康 VUBreakingMax RPS主要瓶頸
1Go10050074DB 查詢慢
2.NET Core10050021Bcrypt
3Express-TS10050058CPU 密集
4NestJS10050058Bcrypt
5Express-JS10050053Bcrypt
6FastAPI10050067Bcrypt
7Laravel1005002.3K*Bcrypt
8Spring Boot5010016Bcrypt
9Django5010017Bcrypt

*Laravel 的 2.3K RPS 來自 Nginx 快速拒絕 102,000 個連線。詳見第 33 篇

排名的解讀

健康 VU 定義:error rate < 5% 且 RPS/VU > 0.1(服務沒有堵塞)。

注意看:前 7 名的健康 VU 都是 100、Breaking 都是 500。差異主要在 低 VU 時的延遲和 RPS。這說明了 CRUD 場景的比較,看的是「低負載時誰效率高」,而不是「誰能撐更多人」——因為沒有人能撐超過 500 VU。


低 VU 時的框架差異

在 10 VU(日常流量等級),框架之間的差異最明顯:

框架AvgP95RPS瓶頸標記
FastAPI64ms179ms24健康
Laravel64ms156ms24健康
Express-TS71ms300ms24健康
NestJS86ms434ms23健康
Go112ms342ms22DB 查詢慢
Express-JS120ms223ms15DB 查詢慢
Spring Boot127ms194ms15DB 查詢慢
.NET Core216ms726ms17DB 查詢慢
Django228ms708ms17DB 查詢慢

有趣的事情:

  • FastAPI 和 Laravel 並列最快(64ms)——一個 Python,一個 PHP
  • Go 不是最快的(112ms,排第 5)——goroutine overhead + GORM 的 overhead 比想像中大
  • .NET Core 和 Django 最慢(216ms / 228ms)——BCrypt.Net 的慢和 Django ORM 的 overhead

為什麼 Go 在低 VU 時不是最快

Go 常被說「最快」,但在 10 VU 時 avg 112ms,比 FastAPI 的 64ms 慢 75%。原因:

  1. GORM 的 reflection overhead:GORM 用 reflect 做 struct → SQL mapping,比手寫 SQL 慢
  2. goroutine scheduler overhead:10 VU 太少,goroutine 的優勢還沒顯現
  3. Go 的 bcrypt 不比 PHP 快:兩者都是 ~80ms,但 PHP-FPM 的 process 隔離讓 overhead 更低

Go 的優勢在「高併發時的穩定性」——它到 100 VU 時還有 69 RPS,而很多框架在 100 VU 就開始堵塞了。


高 VU 時:所有人都死了

500 VU 以上,所有框架都進入「服務堵塞」狀態:

框架500 VU Avg500 VU RPS完成的 Requests
Go6.2s742,512
Express-TS8.1s532,507
NestJS7.2s582,832
Laravel12.1s392,368
FastAPI9.8s482,502
Spring Boot22.9s16960
.NET Core51.2s5295
Django34.8s154

Django 在 500 VU 時只完成了 54 個 request——每秒 1 個。.NET Core 好一點但也只有 295 個。

這些數字配上 0% error rate,就是我們在第 24 篇 Grafana中講的「0% Error 假象」。 不是沒有錯誤,是 request 根本送不出去。


共同瓶頸的驗證

規則引擎(第 46 篇會詳細講)在 CRUD 場景中的判定:

框架瓶頸標記觸發規則
GoDB 查詢慢bcrypt/CPU Blocking + DB Connection Pool
.NET CoreBcryptbcrypt/CPU Blocking
Express-TSCPU 密集bcrypt/CPU Blocking
NestJSBcryptbcrypt/CPU Blocking
Express-JSBcryptbcrypt/CPU Blocking
FastAPIBcryptbcrypt/CPU Blocking
LaravelBcryptbcrypt/CPU Blocking
Spring BootBcryptbcrypt/CPU Blocking
DjangoBcryptbcrypt/CPU Blocking

9 個框架全部觸發了 bcrypt/CPU Blocking 規則。 差別只是在標記名稱上——Go 被標成「DB 查詢慢」是因為 goroutine 的排隊模式讓它看起來像 DB 等待,但本質上也是 bcrypt 阻塞了 CPU。


接下來:每個框架各一篇

接下來 5 篇會拆開講每個框架(或同生態的框架放一起)在 CRUD 場景的細節:

  • 第 29 篇:Go — 低延遲王者但天花板一樣低
  • 第 30 篇:Node.js 三兄弟 — Express-TS vs Express-JS vs NestJS
  • 第 31 篇:Python 雙雄 — Django vs FastAPI
  • 第 32 篇:JVM vs CLR — Spring Boot vs .NET Core
  • 第 33 篇:Laravel — Nginx 的快速拒絕是把雙刃劍

每篇會深入該框架的 per-VU 數據、Grafana 圖形分析、以及框架特有的調校建議。


下一篇

Go CRUD:低延遲王者但天花板一樣低 — Go 為什麼在 10 VU 時不是最快、在 100 VU 時開始領先、在 500 VU 時一樣崩潰。goroutine 的併發模型在 CPU-bound 場景下並不是萬能的。


本系列文章

完整 68 篇目錄見 系列首頁

← 上一篇:bcrypt vs argon2 vs scrypt:密碼 Hash 該用哪個 → 下一篇:Go CRUD 壓測:低延遲王者的天花板在哪裡