結論先講
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%,結果完全不同。
排名總覽
| # | 框架 | 健康 VU | Breaking | Max RPS | 主要瓶頸 |
|---|---|---|---|---|---|
| 1 | Go | 100 | 500 | 74 | DB 查詢慢 |
| 2 | .NET Core | 100 | 500 | 21 | Bcrypt |
| 3 | Express-TS | 100 | 500 | 58 | CPU 密集 |
| 4 | NestJS | 100 | 500 | 58 | Bcrypt |
| 5 | Express-JS | 100 | 500 | 53 | Bcrypt |
| 6 | FastAPI | 100 | 500 | 67 | Bcrypt |
| 7 | Laravel | 100 | 500 | 2.3K* | Bcrypt |
| 8 | Spring Boot | 50 | 100 | 16 | Bcrypt |
| 9 | Django | 50 | 100 | 17 | Bcrypt |
*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(日常流量等級),框架之間的差異最明顯:
| 框架 | Avg | P95 | RPS | 瓶頸標記 |
|---|---|---|---|---|
| FastAPI | 64ms | 179ms | 24 | 健康 |
| Laravel | 64ms | 156ms | 24 | 健康 |
| Express-TS | 71ms | 300ms | 24 | 健康 |
| NestJS | 86ms | 434ms | 23 | 健康 |
| Go | 112ms | 342ms | 22 | DB 查詢慢 |
| Express-JS | 120ms | 223ms | 15 | DB 查詢慢 |
| Spring Boot | 127ms | 194ms | 15 | DB 查詢慢 |
| .NET Core | 216ms | 726ms | 17 | DB 查詢慢 |
| Django | 228ms | 708ms | 17 | DB 查詢慢 |
有趣的事情:
- 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%。原因:
- GORM 的 reflection overhead:GORM 用 reflect 做 struct → SQL mapping,比手寫 SQL 慢
- goroutine scheduler overhead:10 VU 太少,goroutine 的優勢還沒顯現
- Go 的 bcrypt 不比 PHP 快:兩者都是 ~80ms,但 PHP-FPM 的 process 隔離讓 overhead 更低
Go 的優勢在「高併發時的穩定性」——它到 100 VU 時還有 69 RPS,而很多框架在 100 VU 就開始堵塞了。
高 VU 時:所有人都死了
500 VU 以上,所有框架都進入「服務堵塞」狀態:
| 框架 | 500 VU Avg | 500 VU RPS | 完成的 Requests |
|---|---|---|---|
| Go | 6.2s | 74 | 2,512 |
| Express-TS | 8.1s | 53 | 2,507 |
| NestJS | 7.2s | 58 | 2,832 |
| Laravel | 12.1s | 39 | 2,368 |
| FastAPI | 9.8s | 48 | 2,502 |
| Spring Boot | 22.9s | 16 | 960 |
| .NET Core | 51.2s | 5 | 295 |
| Django | 34.8s | 1 | 54 |
Django 在 500 VU 時只完成了 54 個 request——每秒 1 個。.NET Core 好一點但也只有 295 個。
這些數字配上 0% error rate,就是我們在第 24 篇 Grafana中講的「0% Error 假象」。 不是沒有錯誤,是 request 根本送不出去。
共同瓶頸的驗證
規則引擎(第 46 篇會詳細講)在 CRUD 場景中的判定:
| 框架 | 瓶頸標記 | 觸發規則 |
|---|---|---|
| Go | DB 查詢慢 | bcrypt/CPU Blocking + DB Connection Pool |
| .NET Core | Bcrypt | bcrypt/CPU Blocking |
| Express-TS | CPU 密集 | bcrypt/CPU Blocking |
| NestJS | Bcrypt | bcrypt/CPU Blocking |
| Express-JS | Bcrypt | bcrypt/CPU Blocking |
| FastAPI | Bcrypt | bcrypt/CPU Blocking |
| Laravel | Bcrypt | bcrypt/CPU Blocking |
| Spring Boot | Bcrypt | bcrypt/CPU Blocking |
| Django | Bcrypt | bcrypt/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 壓測:低延遲王者的天花板在哪裡