12張圖帶你完全理解分佈式事務產生的場景和解決方案!!

寫在前面

寫這篇文章的背景是有個跟我關係不錯的小夥伴去某大型互聯網公司面試,面試官問了他關於分佈式事務的問題,不巧的是他確實對分佈式事務掌握的不是很深刻,面試的結果挺遺憾的。不過,這位小夥伴仍是挺樂觀的,讓我寫寫關於【分佈式事務】的系列文章,他想提高本身關於分佈式事務的短板,那我就寫一個【分佈式事務】專題吧,專題的內容計劃是從原理、框架源碼到企業級實現,這篇文章也算是整個專題的開篇吧。但願可以爲小夥伴們帶來實質性的幫助。java

本地事務

本地事務流程

在介紹分佈式事務以前,咱們先來看看本地事務。首先,咱們先來一張圖。程序員

由上圖,咱們能夠看出,本地事務由資源管理器(好比DBMS,數據庫管理系統)在本地進行管理。面試

本地事務的優缺點

本地事務具有相應的優勢,也有其不足。數據庫

優勢:編程

  • 支持嚴格的ACID屬性。
  • 可靠,事務實現的效率高(只是在本地操做)。
  • 能夠只在RM(資源管理器)中操做事務。
  • 編程模型簡單。

缺點:微信

  • 缺少分佈式事務的處理能力。
  • 數據隔離的最小單元由RM(資源管理器決定),開發人員沒法決定數據隔離的最小單元。好比:數據庫中的一條記錄等。

ACID屬性

提及事務,咱們不得不提的就是事務的ACID屬性。
網絡

  • A(Atomic):原子性,構成事務的全部操做,要麼都執行完成,要麼所有不執行,不可能出現部分紅功部分失
    敗的狀況。
  • C(Consistency):一致性,在事務執行先後,數據庫的一致性約束沒有被破壞。好比:張三向李四轉100元,
    轉帳前和轉帳後的數據的正確狀態叫做一致性,若是出現張三轉出100元,李四帳戶沒有增長100元這就出現了數
    據錯誤,就沒有達到一致性。
  • I(Isolation):隔離性,數據庫中的事務通常都是併發的,隔離性是指併發的兩個事務的執行互不干擾,一個事
    務不能看到其餘事務運行過程的中間狀態。經過配置事務隔離級別能夠避髒讀、重複讀等問題。
  • D(Durability):持久性,事務完成以後,該事務對數據的更改會被持久化到數據庫,且不會被回滾。

分佈式事務

隨着業務的快速發展,網站系統每每由單體架構逐漸演變爲分佈式、微服務架構,而對於數據庫則由單機數據庫架構向分佈式數據庫架構轉變。此時,咱們會將一個大的應用系統拆分爲多個能夠獨立部署的應用服務,須要各個服務之間進行遠程協做才能完成事務操做。架構

咱們可使用下圖來表示剛開始咱們系統的單體架構。併發

上圖中,咱們將同一個項目中的不一樣模塊組織成不一樣的包來進行管理,全部的程序代碼仍然是放在同一個項目中。框架

後續因爲業務的發展,咱們將其擴展爲分佈式、微服務架構。此時,咱們將一個大的項目拆分爲一個個小的能夠獨立部署的微服務,每一個微服務都有本身的數據庫,以下所示。

又好比,在咱們的程序中,常常會在同一個事務中執行相似以下的代碼來完成咱們的需求。

@Transactional(rollbackFor = Exception.class)
public void submitOrder() {
    orderDao.update(); // 更新訂單信息
    accountService.update(); // 修改資金帳戶的金額
    pointService.update(); //  修改積分
    accountingService.insert(); // 插入交易流水
    merchantNotifyService.notify(); // 通知支付結果
}

上述代碼中的業務,僅僅在submitOrder()方法上添加了一個@Transactional註解,這可以在分佈式場景下避免分佈式事務的問題嗎?很顯然是不行的。

若是上述代碼所對應的:訂單信息、資金帳戶信息、積分信息、交易流水等信息分別存儲在不一樣的數據裏,而支付完成後,通知的目標系統的數據一樣是存儲在不一樣的數據庫中。此時就會產生分佈式事務問題。

分佈式事務產生的場景

跨JVM進程

當咱們將單體項目拆分爲分佈式、微服務項目以後,各個服務之間經過遠程REST或者RPC調用來協同完成業務操做。典型的場景就是:商城系統中的訂單微服務和庫存微服務,用戶在下單時會訪問訂單微服務,訂單微服務在生成訂單記錄時,會調用庫存微服務來扣減庫存。各個微服務是部署在不一樣的JVM進程中的,此時,就會產生因跨JVM進程而致使的分佈式事務問題。

跨數據庫實例

單體系統訪問多個數據庫實例,也就是跨數據源訪問時會產生分佈式事務。例如,咱們的系統中的訂單數據庫和交易數據庫是放在不一樣的數據庫實例中,當用戶發起退款時,會同時操做用戶的訂單數據庫和交易數據庫,在交易數據庫中執行退款操做,在訂單數據庫中將訂單的狀態變動爲已退款。因爲數據分佈在不一樣的數據庫實例,須要經過不一樣的數據庫鏈接會話來操做數據庫中的數據,此時,就產生了分佈式事務。

多服務單數據庫

多個微服務訪問同一個數據庫。例如,訂單微服務和庫存微服務訪問同一個數據庫也會產生分佈式事務,緣由是:多個微服務訪問同一個數據庫,本質上也是經過不一樣的數據庫會話來操做數據庫,此時就會產生分佈式事務。

注意:跨數據庫實例場景和多服務單數據庫場景,本質上都是由於會產生不一樣的數據庫會話來操做數據庫中的數據,進而產生分佈式事務。這兩種場景是你們比較容易忽略的。

分佈式事務解決方案

知道了分佈式事務產生的場景後,接下來,咱們就聊聊分佈式事務具體有哪些解決方案。

2PC方案

2PC即兩階段提交協議,是將整個事務流程分爲兩個階段,準備階段(Prepare phase)、提交階段(commit
phase),2是指兩個階段,P是指準備階段,C是指提交階段。

這裏,咱們用MySQL數據庫舉例,MySQL數據庫支持兩階段提交協議,能夠分爲成功和失敗兩種狀況。

成功狀況

失敗狀況

具體流程以下:

準備階段(Prepare phase): 事務管理器給每一個參與者發送Prepare消息,每一個數據庫參與者在本地執行事
務,並寫本地的Undo/Redo日誌,此時事務沒有提交。
(Undo日誌是記錄修改前的數據,用於數據庫回滾,Redo日誌是記錄修改後的數據,用於提交事務後寫入數
據文件)

提交階段(commit phase): 若是事務管理器收到了參與者的執行失敗或者超時消息時,直接給每一個參與者
發送回滾(Rollback)消息;不然,發送提交(Commit)消息;參與者根據事務管理器的指令執行提交或者回滾操
做,並釋放事務處理過程當中使用的鎖資源。

使用2PC方案時,須要注意的是:必須在最後階段釋放鎖資源。

可靠消息最終一致性方案

可靠消息最終一致性方案是指當事務發起方執行完成本地事務後併發出一條消息,事務參與方(消息消費者)必定能
夠接收消息並處理事務成功,此方案強調的是隻要消息發給事務參與方最終事務要達到一致。

事務發起方(消息生產方)將消息發給消息中間件,事務參與方從消息中間件接收消息,事務發起方和消息中間件
之間,事務參與方(消息消費方)和消息中間件之間都是經過網絡通訊,因爲網絡通訊的不肯定性會致使分佈式事
務問題。 因此,咱們在具體方案中會引入消息確認服務和消息恢復服務。

使用可靠消息最終一致性方案時須要注意幾個問題:

  • 本地事務與消息發送的原子性問題。
  • 事務參與方接收消息的可靠性問題。
  • 消息重複消費的問題(須要實現冪等)。

TCC方案

TCC分爲三個階段:

  • Try 階段 是作業務檢查(一致性)及資源預留(隔離),此階段僅是一個初步操做,它和後續的Confirm 一塊兒才能
    真正構成一個完整的業務邏輯。
  • Confirm 階段 是作確認提交,Try階段全部分支事務執行成功後開始執行 Confirm。一般狀況下,採用TCC則
    認爲 Confirm階段是不會出錯的。即:只要Try成功,Confirm必定成功。若Confirm階段真的出錯了,需引
    入重試機制或人工處理。
  • Cancel 階段 是在業務執行錯誤須要回滾的狀態下執行分支事務的業務取消,預留資源釋放。一般狀況下,採
    用TCC則認爲Cancel階段也是必定成功的。若Cancel階段真的出錯了,需引入重試機制或人工處理。

使用TCC分佈式解決方案時須要注意空回滾、冪等、懸掛等問題。

最大努力通知型方案

此種方案主要用於多個不一樣系統以前保證數據的最終一致性,大致以下圖所示。

使用最大努力通知型方案須要注意冪等和數據的回查操做。

好了,今天就到這兒吧,後續咱們會針對每種分佈式事務解決方案進行具體介紹,下期見!!

重磅福利

微信搜一搜【冰河技術】微信公衆號,關注這個有深度的程序員,天天閱讀超硬核技術乾貨,公衆號內回覆【PDF】有我準備的一線大廠面試資料和我原創的超硬核PDF技術文檔,以及我爲你們精心準備的多套簡歷模板(不斷更新中),但願你們都能找到心儀的工做,學習是一條時而鬱鬱寡歡,時而開懷大笑的路,加油。若是你經過努力成功進入到了心儀的公司,必定不要懈怠放鬆,職場成長和新技術學習同樣,不進則退。若是有幸咱們江湖再見!

另外,我開源的各個PDF,後續我都會持續更新和維護,感謝你們長期以來對冰河的支持!!

相關文章
相關標籤/搜索