結論先講
Go 在 CRUD 場景排第一,但它的優勢比想像中小。 10 VU 時 avg 112ms 其實不是最快的(FastAPI 64ms),Go 的優勢是在 50-100 VU 時 RPS 還能成長(64→69),而其他框架已經開始撐不住了。到 500 VU 一樣崩——goroutine 能讓你更有效率地使用 CPU,但不能變出更多 CPU。
Per-VU 數據
| VU | Avg | P95 | RPS | RPS/VU | 狀態 |
|---|---|---|---|---|---|
| 10 | 112ms | 342ms | 22 | 2.2 | 健康 |
| 50 | 423ms | 1,361ms | 64 | 1.28 | 健康 |
| 100 | 1,052ms | 3,254ms | 69 | 0.69 | 警告 |
| 500 | 6,198ms | - | 74 | 0.15 | 堵塞 |
| 1K | 24,107ms | - | 33 | 0.03 | 堵塞 |
| 5K | 11,500ms | - | 4 | 0.001 | 崩潰(僅 162 reqs) |
關鍵轉折點
50 → 100 VU: RPS 從 64 只升到 69(+8%),但 VU 加了 100%。擴展效率急劇下降。P95 從 1.3 秒跳到 3.2 秒——bcrypt 開始排隊了。
500 VU: RPS 到達天花板 74。之後再加 VU,RPS 反而下降。
5K VU: 只完成了 162 個 request。Go 的 net/http 接受了所有連線,但 goroutine 全部在排隊等 CPU 做 bcrypt,結果是「接了但做不完」。
goroutine 在 CPU-bound 下的表現
Go 的賣點是 goroutine——你可以開幾萬個 goroutine 來處理併發請求,每個 goroutine 只佔幾 KB 記憶體。在 I/O-bound 場景(等 DB、等外部 API),goroutine 可以在等待時讓出 CPU 給其他 goroutine,效率極高。
但 bcrypt 是 CPU-bound。goroutine 在算 bcrypt 的時候不會主動讓出 CPU——它在算,不是在等。幾萬個 goroutine 排隊搶 2 顆 CPU,和 50 個 thread 排隊搶 2 顆 CPU 沒有本質區別。
這就是為什麼 Go 在 500 VU 時一樣崩。goroutine 的優勢被 bcrypt 抵消了。
和 I/O-bound 場景的對比
預告一下:在混合場景(70% Read + 20% Write + 10% Auth)中,Go 的健康 VU 從 100 升到 500。因為 70% 的請求是讀取(I/O-bound),goroutine 終於有機會發揮「等待時讓出 CPU」的優勢。
Go 的效能優勢和 I/O 比例成正比。 CPU-bound 比例越高,Go 的優勢越小。
GORM 的 overhead
Go 社群常說「Go 很快因為沒有 runtime overhead」。但 GORM(Go 最流行的 ORM)有不少 overhead:
- Reflection: GORM 用
reflect把 struct 轉成 SQL,每次查詢都有 reflection 成本 - Callback chain: GORM 的 hook system(BeforeCreate、AfterCreate 等)在每次操作時都會走一遍 callback chain
- Auto-migration: 開發時方便,但每次啟動都會查 schema
在 10 VU 時,GORM 的 overhead 讓 Go 比 FastAPI 慢(112ms vs 64ms)。但在高 VU 時,bcrypt 蓋過了 ORM 的差異。
如果不用 GORM 會怎樣
如果用 database/sql + 手寫 SQL,Go 在 10 VU 時大概能到 60-70ms。但我們壓測的目的是比較「框架 + 生態」的整體效能,不是「語言本身」。實際開發中大部分人會用 ORM,所以用 GORM 更有參考價值。
瓶頸標記為什麼是「DB 查詢慢」而非「Bcrypt」
規則引擎把 Go 標記為「DB 查詢慢」。這看起來有點奇怪——我們知道瓶頸是 bcrypt,為什麼標成 DB?
原因是 goroutine 的行為模式:
- goroutine A 開始算 bcrypt(佔 CPU)
- goroutine B 送 DB 查詢(I/O,讓出 CPU)
- goroutine A 還在算 bcrypt(CPU 被佔住)
- goroutine B 的 DB 查詢結果回來了,但拿不到 CPU 繼續處理
- 規則引擎看到的是「DB 相關操作的 waiting time 很長」→ 標記為 DB 查詢慢
本質上是 bcrypt 佔住 CPU,間接拖慢了 DB 相關操作的處理速度。 規則引擎看到的是表象(DB 等待長),不是根因(bcrypt 佔 CPU)。
這是規則引擎的已知限制——它不做因果推斷,只做指標判定。根因分析還是需要人。
如果你的專案用 Go
基於 CRUD 場景的數據,幾個建議:
- 用 native
bcrypt(x/crypto)就好,它已經是最快的了 - 加 Redis session cache:登入後帶 JWT,後續請求不走 bcrypt
- 考慮 GORM 的替代品:
sqlc(SQL → Go code generator)或sqlx(輕量 SQL wrapper),在低 VU 時能快 30-50% - 不要指望 goroutine 解決 CPU-bound 問題:該加機器就加機器
Go 真正的甜蜜點
Go 的優勢不在 CRUD,而在:
- 高併發 I/O:大量 goroutine 同時等 response
- 微服務間通訊:gRPC + protobuf 的效率遠高於 REST + JSON
- 靜態編譯:Docker image 幾 MB,冷啟動 < 100ms
下一篇
Node.js 三兄弟 CRUD — Express-TS、Express-JS、NestJS 用的底層都是 Express.js + PM2 × 4,但 RPS 差距從 24 到 58。TypeScript vs JavaScript、Sequelize vs TypeORM、框架開銷的量化比較。
本系列文章
完整 68 篇目錄見 系列首頁
← 上一篇:CRUD 壓測總覽:9 框架排名與共同天花板 → 下一篇:Node.js 三兄弟 CRUD:Express-TS vs Express-JS vs NestJS