🎯 什麼情境該想到我

當你「想讓多個應用程式即時共享同一份資料、避免資料在系統間複製與延遲」的時候,考慮讓它們讀寫同一個資料庫。

⚙️ 怎麼用(步驟 / 公式)

意圖:讓多個應用程式把資料存進同一個共享資料庫,透過共同的資料模型來整合。

做法 / 結構要點:

  • 各應用直接讀寫同一個資料庫,資料即時可見、由 DB 的交易機制保證一致性。
  • 需要一個各方都同意的統一 schema。
  • 沒有資料搬運或格式轉換問題,因為大家看的是同一份資料。

★★ 這個模式今天的名聲兩極,而兩邊都對

微服務社群:「共享資料庫是頭號反模式」   ← 針對「跨服務邊界共用」
務實派:    「單體用共享資料庫完全正確」  ← 針對「同一個界限內共用」

★ 判準不是「幾個應用」,是「這些應用是不是同一個 bounded context」。
同一個界限內(同一個團隊、同一組不變式、一起部署)→ 共享資料庫是最簡單也最正確的選擇。
跨界限(不同團隊、不同發布節奏)→ schema 變成一份沒人擁有的公共契約(見 內幕交易)。

★ 具體會怎麼壞:四個真實情境

-- ① ★ 沒人敢改欄位
ALTER TABLE orders DROP COLUMN legacy_status;
-- 「誰在用這一欄?」→ 沒人知道,因為有六個服務直接 SELECT *
 
-- ② ★ 一個服務的慢查詢拖垮所有人
SELECT * FROM orders WHERE JSON_EXTRACT(meta,'$.tag') = 'x';   -- 全表掃描
-- → 連線池被吃光,其他服務全部 timeout(見 [[工具-服務容錯設計]])
 
-- ③ ★ 鎖競爭與死鎖
-- A 服務先鎖 orders 再鎖 users;B 服務反過來 → 死鎖
-- 而且你在 A 的程式碼裡看不到 B 的存在
 
-- ④ ★★ 沒有人能回答「這個欄位的不變式是什麼」
-- orders.status 只能從 pending → paid,但六個服務各自寫入,
-- 於是一定有一個會寫出 pending → shipped

★ 第 ④ 點是最根本的:不變式必須由單一擁有者強制。
共享資料庫把「誰負責保證資料正確」這個問題變成無主的。

★ 中間路線(比「全部拆開」務實)

做法說明
★ 一張表只有一個寫入者其他人可以讀,但只能透過 view/唯讀帳號。寫入權就是所有權
★ schema per service,同一個 DB 實例共用基礎設施(省成本)但不共用 schema。之後要拆很容易
★ 唯讀副本給報表用分析查詢打副本,不影響線上(見 工具-資料複製)
★★ CDC:把共享資料庫變成事件來源Debezium 讀 WAL/binlog → 發成 Kafka 事件。下游從「直接讀表」變成「訂閱事件」
★ CDC 是遷移路徑上最實用的一招:
  現況:service B 直接 SELECT orders
  步驟一:Debezium 監聽 orders 的 WAL,發成 orders.changed 事件
  步驟二:service B 改成訂閱事件、維護自己的讀模型
  步驟三:撤掉 service B 對 orders 表的權限
  → ★ 每一步都可回退,而且不需要改 service A

(WAL 為什麼能當可靠的變更來源,見 工具-預寫日誌與崩潰復原。)

★ 什麼時候共享資料庫就是對的答案

  • ★ 單體應用(或模組化單體)——本來就是一個部署單元。
  • ★ 需要跨實體的交易與強一致:「扣庫存 + 建訂單」要原子。
    跨服務要做到這件事,代價是 Saga/補償交易(見 工具-分散式事務的取捨)——貴得多。
  • ★ 兩個服務其實該是一個。判準:它們是否總是一起改、一起發布?
  • 讀多寫少的參考資料(國別碼、幣別表)——共用一份唯讀資料沒有壞處。
  • 報表/BI——直接查(副本)比建一條資料管線便宜。

🧪 我實際套用的紀錄

  • (待填)

⚠️ 注意 / 什麼時候不適用

  • 高耦合:共用的 schema 變成所有應用的公共契約,難以獨立演進,改一個欄位可能牽動全部。

  • 效能與鎖競爭:多方同時讀寫同一庫可能造成瓶頸與死鎖。

  • 難以整合外部或既有系統(它們各有自己的資料庫),且單一資料庫是共同的單點風險。

  • ★★ 判準是「是不是同一個 bounded context」,不是「幾個應用」。 單體用共享資料庫是對的。

  • ★ 寫入權就是所有權。一張表只讓一個服務寫,是最便宜也最有效的邊界。

  • ★ 跨服務共用 schema = 一份沒人擁有的公共契約,改一個欄位要協調所有人(見 內幕交易)。

  • ★ 不變式必須由單一擁有者強制。六個服務各自寫 status,一定有一個會寫錯。

  • ★ 一個服務的慢查詢會吃掉共用連線池,拖垮所有人。

  • 拆分前先做 CDC(Debezium 讀 WAL)。它讓遷移可以一步一步、每步可回退。

  • 跨服務強一致的代價是 Saga,比共享資料庫貴得多。先確認你真的需要拆。

  • 不要為了「微服務」而拆。判準是團隊邊界與發布節奏,不是架構時尚。

🔗 相關工具