結論先講

Spring Boot 在 CRUD 場景吃虧,在混合場景翻盤;.NET Core 在 CRUD 場景排名好看,但到高 VU 崩得最慘。 兩個企業級框架的故事不是「誰快誰慢」,而是「各自的軟肋在不同場景被放大」。


配置對比

項目Spring Boot.NET Core
RuntimeJVM (Java 17)CLR (.NET 8)
HTTP ServerTomcat (embedded)Kestrel
Workers50 threads (Tomcat)Async thread pool (auto)
ORMHibernate (Spring Data JPA)Entity Framework Core
DB Pool5020
HashjBCrypt (Java)BCrypt.Net (C#)

Spring Boot 的 DB Pool 50 比 .NET Core 的 20 大。Tomcat 的 50 threads 是刻意從預設 200 降低的——因為 2 核 CPU 上 200 threads 反而會因為 context switching 變慢。


Per-VU 數據

VUSpring AvgSpring RPS.NET Avg.NET RPS
10127ms15216ms17
502,582ms151,935ms21
100堵塞 (5.5s)154,417ms20
500堵塞 (22.9s)16堵塞 (51.2s)5
5K堵塞 (22.2s)16Err 99.1% (5xx)5

Spring Boot:RPS 不會成長

Spring Boot 最詭異的地方:從 10 VU 到 10K VU,RPS 始終是 15-16。不會上也不會下。

這像是被一個硬上限卡住了。那個硬上限就是 bcrypt——2 核 CPU 上,jBCrypt 的 throughput 就是每秒 15-16 次。Tomcat 的 50 threads 全在排隊等 CPU 做 hash。

.NET Core:高 VU 崩得最慘

.NET Core 在 500 VU 時的表現是全場最差:

  • avg 51.2 秒(是的,一個 API 請求平均要等 51 秒)
  • 只完成 295 個 request
  • RPS 只有 5

原因是 BCrypt.Net 的 C# 實作特別慢(第 26 篇有詳細分析),加上 async thread pool 在 CPU-bound 操作下的效率不如 Tomcat 的 thread pool。

到 5K VU 更慘:99.1% 的 request 回 5xx。Kestrel 接受了連線但 CLR thread pool 處理不過來,直接噴 Internal Server Error。


JVM Warm-up 效應

Spring Boot 在 10 VU 時 avg 127ms,但這包含了 JVM 的 warm-up 成本。JIT compiler 需要觀察一段時間的 hot path 才能做優化。

在我們的壓測中,10 VU 階段只跑 3 分鐘。 前 30 秒是 warm-up,後 2.5 分鐘才是穩定狀態。如果只看後 2 分鐘的數據,Spring Boot 的 avg 可能降到 100ms 左右。

這是 JVM 系框架在短期壓測中的劣勢。但在生產環境(持續運行),JIT 會把 hot path 優化到接近 native 速度——這就是為什麼 Spring Boot 在混合場景(長時間、大量讀取)中表現翻盤的原因。


Hibernate vs Entity Framework

Hibernate 的優勢:L2 Cache

Hibernate 有 Second Level Cache——跨 session 的查詢快取。如果 User A 和 User B 查詢同一個 Post,第二次查詢直接從 cache 拿,不打 DB。

在 CRUD 場景中這個優勢不明顯(每次都是新 user),但在混合場景(大量讀取同一批 Post)中效果顯著。

Entity Framework 的問題:Change Tracking

EF Core 預設啟用 change tracking——每次查詢都會把結果存進 context,追蹤有沒有被修改。在壓測場景中,大量 context 會吃記憶體、增加 GC 壓力。

// 可以關閉 tracking 提升讀取效能
var users = context.Users.AsNoTracking().ToList();

但我們的壓測程式碼沒有特別加 AsNoTracking()——因為大部分開發者也不會加。


為什麼 CRUD 和混合場景排名反轉

CRUD:   Spring Boot 8th / .NET Core 2nd
Mixed:  Spring Boot 1st / .NET Core 4th

Spring Boot 的翻盤因素:

  1. JIT warm-up 在長時間運行中發揮效果
  2. Hibernate L2 Cache 在讀取密集場景中大幅減少 DB 查詢
  3. Tomcat 的 thread pool 在 I/O 密集場景中比 CPU 密集場景有效率

.NET Core 的下降因素:

  1. BCrypt.Net 的慢在任何包含 Auth 的場景都是瓶頸
  2. EF Core 的 change tracking 在大量查詢時吃記憶體
  3. CLR thread pool 的 auto-scaling 在突發流量下反應較慢

實務建議

Spring Boot 團隊

  1. 給足 JVM warm-up: 部署後先用 health check 或 warm-up script 跑幾分鐘
  2. 啟用 Hibernate L2 Cache: 讀取密集場景效果顯著
  3. 調低 Tomcat maxThreads: 2 核 CPU 上 50 就夠了,200 threads 反而慢
  4. 記憶體要給足: JVM heap 至少 512MB,建議 1GB

.NET Core 團隊

  1. 考慮替換 BCrypt.Net: 用 Isopoh.Cryptography.Argon2 或自己 P/Invoke native bcrypt
  2. 讀取操作加 AsNoTracking(): 減少 EF Core 的 tracking overhead
  3. 監控 thread pool: ThreadPool.GetAvailableThreads() 確認沒有 starvation
  4. 不要被 CRUD 排名騙了: .NET Core 排第 2 是因為其他框架比它更慢,不是因為它特別快

下一篇

Laravel CRUD:Nginx 的快速拒絕是把雙刃劍 — Laravel 在 10K VU 時衝出 2.3K RPS(全場最高),但 99.2% 都是錯誤。PHP-FPM + Nginx 的架構讓它的「失敗模式」和其他框架完全不同。


本系列文章

完整 68 篇目錄見 系列首頁

← 上一篇:Python 雙雄 CRUD:Django vs FastAPI 的 GIL 困境 → 下一篇:Laravel CRUD:Nginx 的快速拒絕是把雙刃劍