結論先講
三場測試、三個結論:Queue 看生態不看速度。Redis cache 值得開。Cold Start 選 Go。 不像 CRUD 和混合場景有戲劇性的排名翻盤,這三場測試的結論比較務實——不是「誰最好」,而是「什麼時候用什麼」。
Queue:同步 vs 非同步佇列
測試場景
| 項目 | 設定 |
|---|---|
| 流程 | 建立 Notification → dispatch 到 Queue → Worker 處理 |
| 比較 | 同步(直接處理)vs 非同步(丟 Queue 再處理) |
| 框架 | 9 框架,各自使用生態內的 Queue 方案 |
各框架的 Queue 方案
| 框架 | Queue 方案 | 特性 |
|---|---|---|
| Express-TS / NestJS | BullMQ(Redis-backed) | 成熟、支援 retry/delay/priority |
| Express-JS | Bull(BullMQ 前身) | 穩定但功能較少 |
| Django | Celery(Redis/RabbitMQ) | Python 生態標準 |
| FastAPI | Celery 或 arq | asyncio 友好 |
| Go | goroutine + channel 或 asynq | 不需要外部依賴 |
| Spring Boot | Spring Async + thread pool | JVM 原生支援 |
| Laravel | Laravel Queue(Redis/DB) | 框架內建 |
| .NET Core | Hangfire 或 MassTransit | 企業級 |
結論:差異在生態不在速度
所有框架都能處理同步和非同步佇列任務。非同步化的好處不是「更快」,而是「讓 response time 更短」——API 立刻回 202 Accepted,實際工作在背景完成。
同步: Client → API → 處理任務(500ms) → 回 200
非同步: Client → API → 丟 Queue(5ms) → 回 202 → Worker 背景處理(500ms)
對用戶來說,API response time 從 500ms 降到 5ms。 但任務完成時間沒變。
Queue 的真正用途
Queue 不是用來「加速」的,是用來:
- 削峰: 大促期間 1000 個訂單同時進來,API 立刻回 202,Queue 慢慢處理
- 解耦: 下訂單和發 email 分開,email 服務掛了不影響訂單
- 重試: Worker 處理失敗可以自動重試,不需要用戶重新操作
- 延遲任務: 30 分鐘後自動取消未付款訂單
選 Queue 的建議
- 已經有 Redis → BullMQ(Node.js)或 Laravel Queue(PHP)
- 需要複雜路由 → RabbitMQ
- 需要事件重播 → Kafka(回顧 Storage 篇)
- Go → 簡單場景用 goroutine + channel,複雜場景用 asynq(Redis-backed)
Redis Cache On/Off:開了就是比較快
測試場景
| 項目 | 設定 |
|---|---|
| 流程 | 同一個 User CRUD workload |
| 比較 | CACHE_ENABLED=true vs CACHE_ENABLED=false |
| 框架 | 9 框架 |
結果
開啟 Redis cache 後,所有框架的讀取效能都有明顯提升。差異跟各框架的 Redis client 效率有關。
這和 Infra 免費午餐篇 的 cache-aside 數據一致:
| 場景 | 無 Cache | 有 Redis Cache | 提升 |
|---|---|---|---|
| 讀多(80:20) | 2,723 req/s | 17,833 req/s | 6.5 倍 |
| 標準 CRUD(50:50) | 2,614 req/s | 3,266 req/s | 25% |
但對 bcrypt 場景效果有限
B2E 的 Redis On/Off 測試是在 Auth + CRUD workload 上做的(包含 bcrypt)。Redis cache 幫助的是讀取操作(跳過 DB 查詢),但幫不了 bcrypt——密碼 hash 是 CPU 計算,不是 DB 查詢。
回顧 Bcrypt 篇 的結論:Redis cache 對 auth 場景的影響 < 5%,但對讀取場景關鍵。 你的應用讀寫比越高,Redis 的效果越大。
各框架的 Redis Client
| 框架 | Redis Client | 備註 |
|---|---|---|
| Express-TS / NestJS | ioredis | 最成熟,支援 cluster |
| Django | django-redis + redis-py | 需要額外裝 |
| FastAPI | aioredis 或 redis-py[async] | async 支援 |
| Go | go-redis | 原生支援 |
| Spring Boot | spring-data-redis + Lettuce | 框架內建 |
| Laravel | predis 或 phpredis | 框架內建 |
| .NET Core | StackExchange.Redis | 成熟 |
Cold Start:Go 最快、NestJS 最慢
測試場景
| 項目 | 設定 |
|---|---|
| 定義 | 從 docker compose up 到第一個 health check 回 200 的時間 |
| 包含 | container 啟動 + runtime 初始化 + DB 連線 + 第一個請求處理 |
結果
| 框架 | Cold Start | 備註 |
|---|---|---|
| Go | 6.7 秒 | 靜態編譯 binary,啟動即運行 |
| .NET Core | 6.7 秒 | CLR 預編譯 (ReadyToRun) |
| Express-JS | ~10 秒 | Node.js 啟動 + require() |
| Express-TS | ~12 秒 | 同上,但多 TypeScript 編譯快取 |
| Django | ~12 秒 | Python import + migration check |
| FastAPI | ~12 秒 | 同 Django |
| Laravel | ~15 秒 | PHP-FPM + OPcache warm-up |
| Spring Boot | ~20 秒 | JVM 啟動 + Spring context 初始化 |
| NestJS | 30.3 秒 | PM2 啟動 4 個 workers,每個都要初始化 |
為什麼 NestJS 最慢
NestJS 本身的啟動不慢(單 instance 約 8 秒),但我們的配置用 PM2 cluster mode 啟動 4 個 workers。4 個 Node.js process 各自:
- 載入 NestJS 框架
- 掃描所有 Module 和 Provider
- 初始化 DI container
- 建立 DB 連線
- 回報 ready
4 個 process × 每個 ~8 秒 ≈ 30 秒(不是並行的,因為 CPU 被搶)。
為什麼 Go 和 .NET Core 一樣快
兩者都是「編譯成 native code」的思路:
- Go: 靜態編譯成單一 binary,沒有 runtime 啟動成本
- .NET Core: ReadyToRun (R2R) 預編譯,跳過大部分 JIT
Spring Boot 慢是因為 JVM 啟動 + Spring 的 annotation scanning + bean 初始化。這是 Java 生態的老問題——GraalVM Native Image 可以改善但目前相容性不夠好。
對 Serverless 的意義
| 部署模式 | Cold Start 影響 |
|---|---|
| 傳統伺服器(長時間運行) | 無影響——啟動一次跑幾個月 |
| 容器化(K8s auto-scaling) | 中等——新 Pod 啟動時用戶會等 |
| Serverless(每次請求可能 cold start) | 關鍵——每個 cold start 都是用戶體驗損失 |
如果你的目標是 Serverless / Edge Function:
- 選 Go:6.7 秒 container 啟動,但 Serverless 場景的 binary cold start < 100ms
- 避免 Spring Boot:20 秒 container 啟動,JVM cold start 幾秒
- 避免 NestJS PM2 mode:30 秒太長。如果非要用 NestJS,改單 instance 模式
三場測試的共同 takeaway
| 測試 | 結論 | 行動項目 |
|---|---|---|
| Queue | 差異在生態不在速度 | 已有 Redis 就用 BullMQ/Laravel Queue |
| Redis Cache | 開了都比較快 | 讀多場景一定要開 |
| Cold Start | Go 最快,NestJS 最慢 | Serverless 選 Go |
這三場不像 CRUD 和混合場景那樣有戲劇性排名翻盤。它們的價值在補完選型地圖——回顧 容量規劃表,現在每一格的建議都有數據支撐了。
系列接下來
B2E 的 10 項測試全部覆蓋完畢。接下來可以寫的:
- 微服務經驗:從單體到微服務,壓測突然變重要的故事
- 系列回顧:壓測教會我們的事
下一篇
從單體到微服務:UV 10 就報錯,才知道要拆 — B2E 的 10 項測試全部跑完。接下來講我們自己的故事——從單體拆成微服務踩了哪些坑、壓測平台怎麼誕生的。
本系列文章
完整 68 篇目錄見 系列首頁
← 上一篇:WebSocket 與 SSE 壓測:所有框架都能撐,差異不在這裡 → 下一篇:從單體到微服務:UV 10 就報錯,才知道要拆