結論先講

Go 在 CRUD 場景排第一,但它的優勢比想像中小。 10 VU 時 avg 112ms 其實不是最快的(FastAPI 64ms),Go 的優勢是在 50-100 VU 時 RPS 還能成長(64→69),而其他框架已經開始撐不住了。到 500 VU 一樣崩——goroutine 能讓你更有效率地使用 CPU,但不能變出更多 CPU。


Per-VU 數據

VUAvgP95RPSRPS/VU狀態
10112ms342ms222.2健康
50423ms1,361ms641.28健康
1001,052ms3,254ms690.69警告
5006,198ms-740.15堵塞
1K24,107ms-330.03堵塞
5K11,500ms-40.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:

  1. Reflection: GORM 用 reflect 把 struct 轉成 SQL,每次查詢都有 reflection 成本
  2. Callback chain: GORM 的 hook system(BeforeCreate、AfterCreate 等)在每次操作時都會走一遍 callback chain
  3. 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 的行為模式:

  1. goroutine A 開始算 bcrypt(佔 CPU)
  2. goroutine B 送 DB 查詢(I/O,讓出 CPU)
  3. goroutine A 還在算 bcrypt(CPU 被佔住)
  4. goroutine B 的 DB 查詢結果回來了,但拿不到 CPU 繼續處理
  5. 規則引擎看到的是「DB 相關操作的 waiting time 很長」→ 標記為 DB 查詢慢

本質上是 bcrypt 佔住 CPU,間接拖慢了 DB 相關操作的處理速度。 規則引擎看到的是表象(DB 等待長),不是根因(bcrypt 佔 CPU)。

這是規則引擎的已知限制——它不做因果推斷,只做指標判定。根因分析還是需要人。


如果你的專案用 Go

基於 CRUD 場景的數據,幾個建議:

  1. 用 native bcrypt(x/crypto)就好,它已經是最快的了
  2. 加 Redis session cache:登入後帶 JWT,後續請求不走 bcrypt
  3. 考慮 GORM 的替代品sqlc(SQL → Go code generator)或 sqlx(輕量 SQL wrapper),在低 VU 時能快 30-50%
  4. 不要指望 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