結論先講
連線池大小是最容易被忽略的效能瓶頸。 pool=5 在 10 人同時用的時候完全夠用,但在 500 人同時用的時候會成為天花板。更容易踩到的坑是水平擴展——你加了 4 台 server 但沒擴 DB 連線池,等於 4 台 server 搶同一個 pool,效能反而變差。
連線池大小:pool=5 vs 20 vs 50
| Pool Max | 低壓 RPS | 高壓 RPS | 對比 |
|---|---|---|---|
| 5 | 1,736 | 1,736 | 基準 |
| 20 | 1,850 | 2,450 | +41% |
| 50 | 1,870 | 2,945 | +70% |
低壓下都一樣
10-50 人同時用的時候,pool=5 和 pool=50 的效能幾乎一樣。因為 5 個連線就夠用了——request 來了拿一個連線、做完查詢還回去,下一個 request 再拿。
高壓下差距很大
500 人同時用的時候,pool=5 代表最多 5 個查詢同時跑,其他 495 個 request 在排隊等連線。pool=50 可以同時跑 50 個查詢。
為什麼不直接設 pool=1000
因為 DB 端有 max_connections 限制。PostgreSQL 預設 100、MySQL 預設 151。
每個連線在 DB 端都佔記憶體(PG 每個連線約 5-10 MB)。1000 個連線 = 5-10 GB RAM 給 DB,還沒算資料快取。
水平擴展的數學
1 台 server × pool=20 = 20 個 DB 連線 ✅
2 台 server × pool=20 = 40 個 DB 連線 ✅
4 台 server × pool=20 = 80 個 DB 連線 ⚠️ 接近 max_connections=100
8 台 server × pool=20 = 160 個 DB 連線 ❌ 超過 max_connections
這就是為什麼 Infra 層的壓測顯示「4 台不會比 2 台更好」。 瓶頸從 application 轉移到了 DB 連線池。
解法:
- 調大 DB 的
max_connections(需要給 DB 更多 RAM) - 用 PgBouncer 做連線池代理(application 連 PgBouncer,PgBouncer 連 DB)
- 減少每台 server 的 pool 大小(trade-off:單台效能下降)
MongoDB:快在哪、慢在哪
純 CRUD:MongoDB 8.6 倍快
| DB | CRUD RPS |
|---|---|
| MongoDB | 1,695 |
| PG (ORM) | 197 |
| PG (Raw SQL) | 215 |
MongoDB 的 BSON 格式天生適合 JSON-like 的 CRUD——不需要 schema mapping、不需要 SQL parse、直接讀寫 document。
但 Transaction 不行
MongoDB 4.0+ 支援 multi-document transaction,但:
- 效能比 PG/MySQL 差很多
- 不支援跨 shard 的 transaction(sharded cluster)
- 語法不如 SQL 直覺
MongoDB 適合什麼
| 場景 | MongoDB | PG |
|---|---|---|
| 用戶 profile(一個 document) | 最佳 | 可以(JSONB) |
| 商品目錄(結構不固定) | 最佳 | 可以(JSONB) |
| 訂單系統(需要 Transaction) | 不推薦 | 最佳 |
| 報表查詢(JOIN + GROUP BY) | 不推薦 | 最佳 |
| 日誌收集(大量寫入) | 好 | MySQL 更好 |
PG JSONB vs MongoDB 的有趣數字
在 Storage 層的測試中,PG JSONB 和 MongoDB 存同一筆 JSON 的速度只差 4%。也就是說,如果你只是想「在資料庫裡存 JSON」,PG 的 JSONB 就夠了,不需要額外維護一個 MongoDB。
MongoDB 真正快的是「純 document 操作、不需要 schema、不需要 Transaction」的場景。
連線池和後端框架的關係
回顧 Python 雙雄 的數據:Django pool=10 vs FastAPI pool=50。Django 在高 VU 時排最後——不全是 Django 的問題,pool=10 是很大的一個因素。
| 框架 | Pool Max | 備註 |
|---|---|---|
| Express-TS/JS | 20 | Sequelize 預設 |
| NestJS | 20 | TypeORM 預設 |
| Django | 10 | CONN_MAX_AGE 機制不同 |
| FastAPI | 50 | SQLAlchemy 可配 |
| Go (GORM) | 20 | SetMaxOpenConns |
| Spring Boot | 50 | Tomcat pool |
| .NET Core | 20 | EF Core 預設 |
| Laravel | - | PHP-FPM 每 request 新連線 |
Spring Boot 的 pool=50 是它在混合場景表現好的原因之一。Django 的 pool=10 是它在高 VU 表現差的原因之一。
框架選型時,不要只看框架本身的效能,也要看它的預設 DB 配置。
DB 層小結
三篇 DB 的核心 takeaway:
- PG vs MySQL 看場景:JSON 查詢選 PG(3.2 倍),大量寫入選 MySQL(2.5 倍)(第 36 篇)
- Bulk INSERT 比換 DB 效果大:26 倍提升,改一行程式碼(第 37 篇)
- 連線池要跟著擴展一起算:pool=5 低壓夠用,高壓差 70%(本篇)
- MongoDB 快但有侷限:純 CRUD 8.6 倍,Transaction 不如 PG
- ORM 沒你想的慢:和 Raw SQL 差不到 10%
接下來進入 Storage 層——Redis、Elasticsearch、Kafka 這些「DB 以外的儲存」在壓測中表現如何。
下一篇
Redis 快 2.4 倍、ES 快 2.6 倍:每種儲存的甜蜜點 — Redis 做 KV 每秒 5,346 個請求、Elasticsearch 做全文搜尋比 PG LIKE 快 2.6 倍、Kafka vs RabbitMQ vs Redis Pub/Sub。不是「裝越多越好」,是「知道什麼時候該加什麼」。
本系列文章
完整 68 篇目錄見 系列首頁
← 上一篇:Bulk Insert 快 26 倍:DB 寫入的正確姿勢 → 下一篇:Redis 快 2.4 倍、ES 快 2.6 倍:每種儲存的甜蜜點