結論先講
PostgreSQL 和 MySQL 各有主場。 PG 贏在 JSON 查詢(3.2 倍)、交易處理(天花板高 40%)、生態完整性(JSONB 讓你不用加 MongoDB)。MySQL 贏在大量寫入(2.5 倍)——如果你的場景是 80% 寫入,MySQL 真的比較快。大部分場景選 PG;寫入密集選 MySQL。
這一步怎麼來的:後端壓測發現 bcrypt 之外,DB 查詢是第二大效能影響因素。Go 的瓶頸被標記為「DB 查詢慢」,Django 的 pool=10 直接拖垮高 VU 表現。於是固定後端框架,只換 DB 來測。
為什麼測 DB 層
後端框架壓測發現 bcrypt 是共同瓶頸(第 6 篇)。但 bcrypt 之外,DB 查詢是第二大效能影響因素。Go 的瓶頸 被標記為「DB 查詢慢」,Django 的 pool=10 直接拖垮高 VU 表現。
這三篇 DB 測試把後端框架固定(Express-TS + Sequelize),只換 DB 變因,回答:PG vs MySQL 差多少?ORM 真的慢嗎?怎麼寫入最快?連線池開多大?
測試怎麼做的
跟後端框架壓測不同——DB 層的測試固定同一個後端,只換 DB:
| 項目 | 設定 |
|---|---|
| 後端 | Express-TS(固定不變) |
| ORM | Sequelize(固定不變) |
| Docker | 4 CPU / 4 GB |
| DB Pool | 10 |
| Cache | 關閉 |
| 比較對象 | PostgreSQL vs MySQL × 不同場景 |
這才是公平比較——改一個變因,其他全部不動。和 第 3 篇控制變因 講的原則一致。
7 項 DB 測試結果
1. ORM vs Raw SQL(讀寫 50:50)
| 方式 | PG RPS | MySQL RPS |
|---|---|---|
| Sequelize ORM | 197 | 183 |
| Raw SQL | 215 | 198 |
ORM 和手寫 SQL 的差距不到 10%。 很多人以為 ORM 很慢,實測差距沒有想像中大。ORM 的價值在開發速度和可維護性,這 10% 的效能損失完全可以接受。
但注意:這是「簡單 CRUD」的數字。複雜查詢(JOIN 3 張表 + subquery + GROUP BY)時,ORM 生成的 SQL 可能遠不如手寫 SQL 優化。建議:80% 用 ORM,20% 複雜查詢手寫 SQL。
2. JSON/JSONB 欄位查詢
| DB | JSON 查詢 RPS | 對比 |
|---|---|---|
| PG JSONB | 645 | 3.2 倍 |
| MySQL JSON | 201 | 基準 |
PG 的 JSONB 支援 GIN 索引,查詢 JSON 欄位可以走索引。MySQL 的 JSON 欄位查詢需要每次都 parse JSON——等於全表掃描。
這是 PG 最大的優勢。 如果你的應用需要存 JSON(用戶設定、產品規格、表單欄位),PG 一個資料庫就搞定,不用另外加 MongoDB。
3. 寫入密集場景(讀寫 20:80)
| DB | RPS |
|---|---|
| MySQL | 498 |
| PG | 199 |
MySQL 在大量寫入時比 PG 快 2.5 倍。
原因:PG 的 MVCC(Multi-Version Concurrency Control)在每次寫入時都會產生一個新版本的 row,舊版本需要 VACUUM 清理。大量寫入 = 大量垃圾版本 = VACUUM 壓力大。
MySQL 的 InnoDB 也用 MVCC,但它的 undo log 機制在高頻寫入時效率更好。
4. Bulk Insert 策略
| 方式 | RPS | 對比 |
|---|---|---|
| 逐筆 INSERT × 100 | 8 | 基準 |
| ORM bulk | 89 | 11 倍 |
| Raw multi-value INSERT | 208 | 26 倍 |
批次寫入比逐筆快 26 倍。 這個差距比「選哪個 ORM」或「選哪個 DB」的影響都大。
-- 逐筆:100 個 INSERT,100 個 roundtrip
INSERT INTO posts (title, body) VALUES ('A', '...');
INSERT INTO posts (title, body) VALUES ('B', '...');
-- ... 重複 100 次
-- 批次:1 個 INSERT,1 個 roundtrip
INSERT INTO posts (title, body) VALUES
('A', '...'),
('B', '...'),
-- ... 100 筆
;大部分 ORM 都支援 bulk insert(Sequelize 的 bulkCreate、TypeORM 的 insert().values([...])),但開發者常常忘記用。
5. Index 策略(100K rows)
| Index 狀態 | 讀取 RPS | 寫入 RPS |
|---|---|---|
| 無 Index | 慢 | 快 |
| 正確 Index | 最佳平衡 | 稍慢 |
| 過度 Index | 讀取不變 | 明顯慢 |
過度 Index(每個欄位都建 index)會拖慢寫入——每次 INSERT/UPDATE 都要同時更新所有 index。
6. Transaction 鎖競爭(50 hot rows)
| DB | Max RPS | 備註 |
|---|---|---|
| PG | 高 40% | MVCC 讀不阻塞寫 |
| MySQL | 基準 | InnoDB gap lock 較積極 |
PG 的 MVCC 讓讀取不會被寫入阻塞——讀舊版本就好。MySQL 的 InnoDB 在某些場景下會用 gap lock,讀寫互相阻塞。
7. MongoDB vs SQL
| DB | CRUD RPS | 對比 |
|---|---|---|
| MongoDB | 1,695 | 8.6 倍 |
| PG ORM | 197 | 基準 |
MongoDB 在純 CRUD(無 JOIN、無 Transaction)場景快 8.6 倍。但 MongoDB 4.0+ 的 multi-document transaction 效能不如 PG/MySQL,也不適合複雜的多表關聯查詢。
選 DB 的決策樹
你的場景是什麼?
├── 大部分是讀取 + 需要 JSON 查詢 → PostgreSQL
├── 大量寫入(日誌、IoT、事件紀錄) → MySQL
├── 純文件式存取、不需要 Transaction → MongoDB
├── 需要全文搜尋 → PostgreSQL + Elasticsearch
└── 不確定 → PostgreSQL(最通用)
和 Infra 層的連結
DB 選好了,接下來影響效能的是 怎麼連:
- 連線池大小: pool=5 vs pool=50 在高壓下差 70%(第 38 篇 會講)
- Redis 快取: 加 Redis 做 cache-aside,讀取場景提升 6.5 倍
- 讀寫分離: PG primary + replica,讀多場景有明顯效果
這些是 Infra 層 的主題。
下一篇
Bulk Insert 快 26 倍:DB 寫入的正確姿勢 — ORM vs Raw SQL 差 10%,但逐筆 vs 批次差 26 倍。Index 過多寫入變慢。Transaction 鎖怎麼避免。寫入優化的實戰指南。
本系列文章
完整 68 篇目錄見 系列首頁
← 上一篇:CSR vs SSR vs Islands:首屏速度的三種哲學 → 下一篇:Bulk Insert 快 26 倍:DB 寫入的正確姿勢