在微服務架構中,隨著服務的逐步拆分,數(shù)據(jù)庫私有已經(jīng)成為共識。
隨著用戶訪問量的逐漸上漲,數(shù)據(jù)庫甚至是服務的分片、分區(qū)、水平拆分、垂直拆分已經(jīng)逐漸成為較為常用的提升瓶頸的解決方案,因此越來越多的原子操作變成了跨庫甚至是跨服務的事務操作。
例如一個電商應用,用戶下單后需要操作訂單表,庫存表和用戶表,如果這三個表是在同一個數(shù)據(jù)庫實例中,可以放在一個事務中來保持一致性:
start transaction
insert into orders...
update inventories...
update users...
commit
如果這三個表是在三個不同的數(shù)據(jù)庫實例中,又如何保證一致性呢?
分布式事務
多個副本 replica,存在不同的物理機器上。
在分布式數(shù)據(jù)庫上,提交或回滾事務的決定必須統(tǒng)一。即要么一起提交,要么一起回滾。
2階提交 Two Phase Commitment
準備階段 Prepare
- 協(xié)調(diào)者(事務管理器)向所有參與者詢問是否可以執(zhí)行提交操作 vote,并等待各個參與者的相應。
- 參與者執(zhí)行所有事務操作,并寫入 redo log 和 undo log。
- 參與者相應詢問:
- 若實際執(zhí)行成功,返回 “同意”
- 若實際執(zhí)行失敗,返回 “中止”
提交階段
-
若所有參與者都返回 “同意”:
- 協(xié)調(diào)者(事務管理器)向所有參與者發(fā)出 “正式提交” 的請求。
- 參與者收到請求后,正式提交。
- 參與者返回 “完成”。
- 協(xié)調(diào)者(事務管理器)收到所有的 “完成” 消息后,完成事務。
-
若有任何一個參與者返回 “中止”,或詢問超時(即有部分參與者沒有相應):
- 協(xié)調(diào)者(事務管理器)向所有參與者發(fā)出 “回滾” 的請求。
- 參與者收到請求后,利用 undo log 進行回滾。
- 參與者返回 “回滾完成”。
- 協(xié)調(diào)者(事務管理器)收到所有的 “回滾完成” 消息后,取消事務。
缺點:
- 所有參與者都是事務阻塞的。
- 單點故障。協(xié)調(diào)者一旦故障,則無法進行。
- 數(shù)據(jù)不一致。在第二個階段,協(xié)調(diào)者(事務管理器)向所有參與者發(fā)出 “正式提交” 或者 “回滾” 的請求,部分參與者可能由于網(wǎng)絡問題收不到。從而導致有的參與者提交,有的沒有提交。
分布式系統(tǒng)中的所有通信均存在著三種狀態(tài):成功,失敗,超時。其中,超時狀態(tài)的存在是我們在設計分布式系統(tǒng)時所面對的永遠的痛,2PC 同樣存在問題,尤其是在發(fā)送完可以提交的指令后,參與者在沒有收到提交或者回滾的指令時,面對已經(jīng)上鎖的資源,面對已經(jīng)寫出去的 undo log 或者 redo log,參與者會一時陷入手足無措的狀態(tài),為了解決這個問題,3PC 應運而生。
3階提交 Three Phase Commitment
3PC 在 commit 之前增加了 preCommit 的過程,使得在參與者在收不到確認時,依然可以從容 commit 或者 rollback,避免資源鎖定太久導致浪費。但是 3PC 同樣存在著很多問題。實現(xiàn)起來非常復雜,因為很難通過多次詢問來解決系統(tǒng)間分歧問題,尤其是存在超時狀態(tài)互不信任的分布式網(wǎng)絡中,這也就是著名的拜占庭將軍問題。
MySQL 分布式事務
XA 事務就是兩階段提交的一種實現(xiàn)方式。
MySQL InnoDB 存儲引擎提供了對 XA 事務的支持,并通過 XA 事務來支持分布式事務的實現(xiàn)。
XA 事務由如下組成:
- 一個或多個資源管理器:提供訪問事務資源的方法。通常一個數(shù)據(jù)庫就是一個資源管理器。 例如 MySQL 數(shù)據(jù)庫。
- 一個事務管理器:協(xié)調(diào)參與全局事務中的各個事務。需要和參與全局事務的所有資源管理器進行通信。 例如連接 MySQL 服務端的客戶端。
- 一個應用程序:定義事務的邊界,指定全局事務中的操作。