結論先講

三個配置改動的效果加起來,比換框架大。 Redis cache 在讀多場景提升 6.5 倍(2,723 → 17,833 req/s),nginx keepalive +7%(零成本),PM2 多 worker +93%(4,888 → 9,461 req/s)。Django + 這三個優化的效能,可能比「裸的 Go」還好。

這一步怎麼來的:框架選了、DB 選了、Storage 也知道什麼時候加。但還有一層——nginx、multi-worker、水平擴展這些「不改程式碼只改配置」的東西,效果有多大?答案讓我們自己都嚇到。


為什麼測架構層

前面測了後端框架(Part 3)、DB(Part 5)、儲存(Part 6)。但真實系統不是「框架 + DB」這麼簡單——中間還有 nginx、Redis cache、multi-worker、水平擴展這些「架構層」的配置。

這兩篇 Infra 測試回答的問題是:不改程式碼,只改配置,能提升多少? 答案是:比換框架還多。


為什麼叫「免費午餐」

後端框架壓測(Part 3)告訴我們 Go 最快、Django 最慢。很多人的第一反應是「那我們換 Go 吧」。

但換框架的成本:

  • 團隊學新語言(3-6 個月)
  • 重寫所有 API(幾個月到一年)
  • 新框架的 bug 和不熟悉(持續好幾年)

而下面三個優化:

  • 改一行 nginx 配置
  • 加幾行 Redis 程式碼
  • 改一行 PM2 配置

就能得到接近換框架的效果。這就是免費午餐。


免費午餐 #1:Redis Cache-aside(+6.5 倍)

數據

場景無 Cache有 Redis Cache提升
讀多(80% 讀 20% 寫)2,723 req/s17,833 req/s6.5 倍
標準 CRUD(50:50)2,614 req/s3,266 req/s25%

讀多場景 6.5 倍,標準 CRUD 25%。你的應用讀寫比越高,Redis cache 的效果越大。

Cache-aside Pattern 怎麼運作

讀取:
  1. 先查 Redis
  2. Redis 有 → 直接回(快,幾 ms)
  3. Redis 沒有 → 查 DB → 寫入 Redis → 回

寫入:
  1. 寫 DB
  2. 刪 Redis 中的對應 key(不是更新,是刪除)
  3. 下次讀取會 cache miss → 自動從 DB 重新載入

為什麼寫入時「刪除」而不是「更新」Cache?因為如果同時有兩個寫入,「更新 cache」可能造成 race condition——後來的寫入先更新了 cache,但先來的寫入後更新 cache 覆蓋掉了。「刪除」就沒有這個問題。

注意事項

  • Cache 失效策略: TTL(過期時間)要設合理。太短 → cache hit rate 低;太長 → 用戶看到舊資料
  • Cache miss 懲罰: 第一次查詢(cache miss)比沒有 cache 時更慢——因為多了一次 Redis 的 roundtrip。但後續的 cache hit 遠遠補回來
  • 和 bcrypt 的關係: Redis cache 對 auth 場景的影響 < 5%——因為 bcrypt 是 CPU-bound,cache 幫不了 CPU 的忙(呼應 第 25 篇

免費午餐 #2:nginx keepalive(+7%,零成本)

數據

配置RPS
無 nginx(直連 AP)基準
nginx(無 keepalive)-5%(proxy overhead)
nginx + keepalive+7%

為什麼加了中間層反而更快

沒有 keepalive 時,每個 request 都要建新的 TCP 連線:三次握手(SYN → SYN-ACK → ACK)。

開了 keepalive,nginx 和後端 AP 之間維持長連線——第一個 request 建立連線,後續的 request 直接複用同一條連線。省掉的不只是握手時間,還有 TCP slow start 的延遲。

怎麼開

upstream backend {
  server app:3000;
  keepalive 64;  # 維持 64 條長連線
}
 
server {
  location /api/ {
    proxy_pass http://backend;
    proxy_http_version 1.1;
    proxy_set_header Connection "";  # 啟用 keepalive
  }
}

就這幾行,零成本 +7%。


免費午餐 #3:PM2 多 Worker(+93%)

數據

WorkersRPS對比
14,888基準
27,150+46%
49,461+93%

為什麼不是線性增長

1 worker → 4 workers,理論上應該快 4 倍(+300%),但實際只有 +93%。原因:

  1. CPU 核心數限制: Docker 只有 4 核,4 workers 已經跑滿。再加 worker 只會更慢(context switching)
  2. DB 連線池共享: 4 workers 搶同一個 pool,連線等待時間增加
  3. OS scheduling overhead: 多 process 之間的切換有成本

公式

Worker 數 = CPU 核數 × 1-2。4 核 CPU 開 4-8 workers。我們測的環境是 4 核開 4 workers,剛好是甜蜜點。

各語言的對應工具:

  • Node.js: PM2 cluster mode
  • Python: gunicorn —workers 4 / uvicorn —workers 4
  • Java: 不需要(JVM 的 thread pool 天然支援多核)
  • Go: 不需要(goroutine 天然用多核)
  • PHP: PHP-FPM pm.max_children

三者加起來

優化效果成本
Redis cache(讀多)+550%加幾行程式碼
nginx keepalive+7%改一行配置
Multi-worker+93%改一行配置

如果三個都做(現實中它們是乘法關係,不是加法):

基準: 2,723 req/s(單 worker、無 cache、無 nginx)
+ multi-worker: 2,723 × 1.93 = 5,255 req/s
+ nginx keepalive: 5,255 × 1.07 = 5,623 req/s
+ Redis cache: 5,623 × 6.5 = 36,550 req/s(讀多場景)

從 2,723 到 36,550——13 倍提升,不改一行業務程式碼。

同時期 Go 在 CRUD 場景 的 Max RPS 是 74。即使考慮到場景不同,「Django + 三個免費午餐」的效能很可能超過「裸的 Go」


這也是跨層洞察的第 4 條

回顧 Overview 頁面的跨層洞察:

「免費午餐」的效果超過換框架。Redis 快取在讀多場景提升 6.5 倍、nginx keepalive 提升 7%、多 worker 模式提升 93%。這三件事加起來比從 Django 換成 Go 的效果還大。

這條洞察就是本篇的核心。先把架構優化做完,再考慮換框架。 大部分團隊的效能問題不是因為選錯框架,而是因為沒做這三件事。


下一篇

水平擴展的天花板:2 台 +53%,4 台卡 DB — 加機器不是萬能的。壓測顯示 2 台確實提升 53%,但 4 台不會比 2 台更好——瓶頸從 application 轉移到了 DB 連線池。還有 K8s vs Docker Compose 的真實比較:1000 人以內 Docker Compose 就夠了。


本系列文章

完整 68 篇目錄見 系列首頁

← 上一篇:Redis 快 2.4 倍、ES 快 2.6 倍:每種儲存的甜蜜點 → 下一篇:水平擴展的天花板:2 台 +53%,4 台卡 DB