結論先講
做微服務的人很多,做得扎實的很少。 大部分公司的微服務是「先上再說」,遇到問題就加 cache、加 queue、加機器。這些都是合理的暫時解法,但如果從來不回頭解決根因,暫時解法就變成永久的技術債。這個系列 27 篇壓測數據要回答的,就是「根因是什麼、該怎麼正確解決」。
反模式一:Cache 硬撐一切
企業裡常見的做法
系統慢了 → 加 Redis cache → 變快了 → 結案。
三個月後又慢了 → cache hit rate 降了 → 加更多 cache → 又結案。
一年後整個系統的 cache 層比業務邏輯還複雜:cache 失效策略互相衝突、cache warmup 要 30 分鐘、有人不小心 flush 了 Redis 系統直接掛。
壓測數據怎麼說
第 21 篇 的數據:
| 場景 | 無 Cache | 有 Cache | 提升 |
|---|---|---|---|
| 讀多(80:20) | 2,723 | 17,833 | 6.5 倍 |
| Auth 場景 | 基準 | +< 5% | 幾乎沒用 |
Cache 在讀取場景確實神效(6.5 倍),但對 Auth 場景(bcrypt)幫助不到 5%。
問題不在「要不要加 cache」,而在「加 cache 之前你知不知道瓶頸在哪」。
如果瓶頸是 DB 讀取 → 加 cache 正確。 如果瓶頸是 bcrypt CPU → 加 cache 沒用,你需要的是降 bcrypt rounds 或加 CPU。 如果瓶頸是 DB 連線池 → 加 cache 有點用,但更正確的做法是調 pool size(第 19 篇:pool=50 比 pool=5 高壓下快 70%)。
Cache 是止痛藥,不是治療。 用之前先做壓測,確認你在治對的病。
反模式二:Kafka/RabbitMQ 當 REST API 用
企業裡常見的做法
Service A 要通知 Service B「訂單建好了」。正常做法是發一個 event 到 Kafka:
Event: { type: "order.created", orderId: 123 }
但我見過的做法是:把整個 request payload 塞進 Kafka,當成 REST API 在用:
Message: {
method: "POST",
url: "/api/orders",
body: { productId: 456, quantity: 2, userId: 789, ... },
headers: { authorization: "Bearer ..." }
}
Consumer 收到後再「假裝自己是 HTTP server」處理這個 message。
為什麼這很糟
- 失去 Kafka 的優勢: Kafka 的價值在「事件串流」——可以重播、可以多個 consumer 各自處理。把它當 REST 用,等於用飛機跑高速公路
- Debug 噩夢: REST API 有 HTTP status code、有 request/response log。Kafka message 失敗了只有一行 error log,你不知道是 producer 的問題還是 consumer 的問題
- 延遲不可控: REST 是同步的(送出去就知道結果),Kafka 是非同步的。當成 REST 用的話,你還需要另一個機制來拿「處理結果」——通常又是一個 Kafka topic,複雜度翻倍
正確的用法
| 場景 | 用 REST | 用 Event (Kafka/RabbitMQ) |
|---|---|---|
| 需要立刻知道結果 | 查詢商品、登入 | ❌ |
| 通知其他服務「事情發生了」 | ❌ | 訂單建好了、庫存變了 |
| 需要重試/延遲處理 | ❌ | 寄 email、推播通知 |
| 需要多個 consumer | ❌ | 訂單建好 → 通知+庫存+報表同時處理 |
回顧 第 27 篇:Queue 的價值在削峰、解耦、重試,不在「替代 REST」。
反模式三:「我們有微服務」
企業裡常見的做法
把一個 monolith 拆成 5 個 repo,每個 repo 部署一個 container。然後宣布「我們已經是微服務架構了」。
但實際上:
- 5 個 service 共用同一個 DB(同一張 users 表)
- 改 A service 的 schema 會影響 B service
- 部署時 5 個 service 必須一起部署(不然 schema 對不上)
- 沒有 API gateway、沒有 service discovery、沒有 circuit breaker
這不是微服務,這是「分散式單體」(distributed monolith)。 你同時得到了微服務的複雜度和單體的耦合度。
怎麼判斷你是不是分散式單體
問自己三個問題:
- A service 能不能獨立部署? 如果改了 A 必須同時部署 B,你是分散式單體
- A service 掛了,B service 能不能正常運作? 如果 A 掛了 B 也掛,你是分散式單體
- A 和 B 是不是共享 DB schema? 如果是,你是分散式單體
我們自己的經驗:一開始也是共用 DB(上一篇提到的)。這是務實的選擇——先拆 process 再拆 DB。但要意識到這只是過渡階段,不是最終形態。
反模式四:不做壓測就選框架
企業裡常見的做法
(以下是常見場景,不是特定公司)
CTO:「我聯說 Go 很快,我們換 Go 吧。」 技術主管:「但團隊都寫 Python/Node.js…」 CTO:「那就學。」
六個月後:團隊還在學 Go 的 error handling、goroutine leak 到處都是、用 GORM 寫出來的效能還不如之前用熟悉框架的成果。
壓測數據怎麼說
整個系列 27 篇反覆出現的結論:
- 免費午餐(Redis + keepalive + multi-worker)的效果 > 換框架(第 21 篇:組合起來 13 倍提升)
- 前端框架差距只有 6 分(第 15 篇:93~99,看生態不看分數)
- DB 選型看場景不看排名(第 17 篇:PG 贏 JSON 3.2 倍,MySQL 贏寫入 2.5 倍)
- ORM vs Raw SQL 差不到 10%(第 18 篇:但 Bulk Insert 差 26 倍)
「為什麼用 PostgreSQL 不用 MySQL」「為什麼用這個前後端框架」——這些問題不應該靠直覺回答。 跑一次壓測,用數據說話。
這整個壓測平台的存在意義就是:讓「選什麼技術」從「聽說」變成「測過」。
為什麼要做扎實
做微服務做到「看起來像」很容易。做到「真的能 scale」很難。差距在於:
| 「看起來像」 | 「真的做」 |
|---|---|
| 拆了 5 個 repo | 每個 service 能獨立部署 |
| 共用 DB | 每個 service 自己的 DB + event 同步 |
| 加了 Kafka | Event schema 有版本管理 |
| 加了 Redis | 知道 cache invalidation 策略 |
| 跑了壓測一次 | 每次部署前跑 regression 壓測 |
這個系列 27 篇的壓測數據,就是「真的做」的過程紀錄。不是因為我們很厲害,是因為我們踩了太多坑之後決定用數據取代直覺。
下一篇
壓測教會我的事:如果重來一次 — 兩個月蓋平台、42 項測試、27 篇文章。回顧整個過程,最有價值的不是「Go 排第幾」,而是「免費午餐比換框架效果大」「UV 10 就能暴露問題」「先測再選」這些心態上的轉變。
本系列文章
完整 68 篇目錄見 系列首頁
← 上一篇:從單體到微服務:UV 10 就報錯,才知道要拆 → 下一篇:壓測教會我的事:如果重來一次