4.4 拆分後的代價:分散式事務、跨庫 Join 與 MPP#
庫拆完了,煩惱才真正開始#
前三節一路拆:讀寫分離、垂直分庫、水平分片,效能是上去了。但程式設計師開始抱怨:以前一個本地事務搞定的下單扣庫存,現在跨三個庫,一半成功一半失敗怎麼辦?以前一個 JOIN 查出的報表,現在數據散在八個片,怎麼查?本節直面拆分的代價。
代價一:分散式事務#
flowchart LR
APP[下單] --> T1[訂單庫:建單成功]
T1 --> T2[庫存庫:扣減超時?]
T2 --> Q[單子建了,庫存沒扣<br/>數據不一致!]| 方案 | 做法 | 優點 | 代價 |
|---|---|---|---|
| 2PC(兩階段提交) | 協調者先問 all-ready 再 commit | 強一致 | 同步阻塞、協調者單點、效能差 |
| TCC(Try-Confirm-Cancel) | 業務拆三段:預留/確認/取消 | 一致性好、靈活 | 業務侵入大,每個操作寫三遍 |
| Saga | 長事務拆多個本地事務,失敗逐段補償 | 高可用、適合長流程 | 要寫補償邏輯,最終一致非即時 |
| 最終一致(可靠消息) | 本地事務 + MQ:單建完發消息扣庫存 | 效能最高、解耦 | 短暫不一致,需接受延遲 |
淘寶的選擇很務實:核心資金鏈用 TCC/Saga 保底,普通鏈路用「本地事務 + RocketMQ 事務消息」做最終一致(扣款成功後發消息,庫存服務消費到再扣,失敗重試 + 對帳兜底)。記住原則:能接受短暫不一致的,一律用最終一致,別碰 2PC。
代價二:跨庫 Join#
flowchart LR
Q[查:訂單JOIN商品] --> A[應用層組裝:查2次拼起來]
Q --> B[冗餘欄位:下單時抄一份快照]
Q --> C[MPP/數倉:離線匯總查]| 對策 | 適用場景 |
|---|---|
| 應用層組裝 | 線上 TP 查詢,結果集小 |
| 欄位冗餘快照 | 高頻展示欄位(標題、價格快照) |
| ES/寬表同步 | 搜尋、篩選等多維查詢 |
| 離線數倉 | 後台報表、跨全量統計 |
代價三:運維複雜度翻倍#
別只盯著程式碼,運維的 pain 更直接:備份從 1 份變 N 份,慢 SQL 排查要逐片看,某片熱點要單獨擴容,監控儀表從一張變一打。以前一個 DBA 搞定,現在要配分片治理:統一的發號器、分片元數據、跨片追蹤 ID,缺一不可。很多小團隊分片後被運維拖垮,不是效能不夠,而是人手不夠——這也是 4.3 節說「能不分片就不分片」的原因。
flowchart LR
OPS[DBA視角] --> B[備份×N片]
OPS --> S[慢查詢×N片]
OPS --> H[熱點片單獨擴容]救星:MPP 與 NewSQL(TiDB / Greenplum)#
手寫分片太苦,業界造了兩類新輪子:
| 類型 | 代表 | 特點 |
|---|---|---|
| NewSQL(線上事務) | TiDB | 相容 MySQL 協議,自動分片、自動擴容,事務接近單機體驗 |
| MPP(離線分析) | Greenplum / ClickHouse | 列存 + 大規模並行,專吃跨全量報表與 OLAP |
flowchart TD
OLTP[線上:TiDB自動分片<br/>應用以為還是單庫] --> SYNC[Canal/同步]
SYNC --> OLAP[MPP數倉:報表隨便JOIN]路線圖:線上用 NewSQL 藏住分片複雜度,線下用 MPP 承接複雜查詢,中間用同步鏈路連起來。這正是第五、六章服務化與大數據的入口。
本章小結#
- 拆分的三個代價:分散式事務、跨庫 Join、運維複雜度
- 事務選型:2PC 強一致但慢;TCC/Saga 保核心,最終一致扛大流量
- 跨庫 Join:線上組裝 + 冗餘,複雜查詢趕去 ES 與數倉
- NewSQL(TiDB)藏分片、MPP(Greenplum)扛分析,是拆分複雜度的終極收斂
- 第四章全景:讀寫分離 → 垂直分庫 → 水平分片 → 新硬體收斂
想一想#
- 為什麼大促核心鏈路寧願用「最終一致 + 對帳」也不用 2PC?(提示:鎖住全鏈路的代價)
- TCC 的 Try/Confirm/Cancel 三段各在做什麼?以「下單扣庫存」舉例寫出來。
- 什麼查詢該走線上組裝,什麼該趕去 MPP?試為淘寶各舉兩例並說明理由。