硬核!2w 字長文爆肝分散式事務知識點!!

阿寒聊Java發表於2021-06-10

前言

分散式事務,是分散式架構中一個繞不開的話題,而什麼是分散式事務?為什麼要使用分散式事務?分散式事務有哪些實現方案?更是面試時面試官特別喜歡的一個分散式三連炮!同時用XMind畫了一張導圖記錄分散式事務學習筆記(原始檔對部分節點有詳細備註和參考資料, 已經完善更新):

1.事務基本概念

1.1什麼是事務?

事務是恢復和併發控制的基本單位,事務有四個特性(ACID),原子性(Atomicity),一致性(Consistency),隔離性(Isolation),永續性(Durability)。

1.2事務經典場景

假設這樣一個場景:

A 給 B 轉賬 100,流程步驟如下:

  1. A 減 100

  2. B 多 100。

如果第一步驟執行後,系統崩潰掉了。會怎麼樣呢?

問題:A 被減掉了 100,但 B 的錢未能加 100. 此時,A + B 的金錢總額憑空少了 100。資料不一致了。

解決思路呢?我們希望步驟 1 和步驟 2 能夠繫結在一起執行,不可分;並且在步驟 1 和步驟 2執行的過程中,儘量規避中間狀態。即謂事務。 事務在解決上述問題中,提出了以下四種特性

1.2.1原子性

原子性就是不可拆分的特性,要麼全部成功然後提交(commit),要麼全部失敗然後回滾(rollback)若開啟事務,在上述場景就不會出現 A 少 100 成功,B 多 100 失敗 這種情況。MySQL 通過Redo Log 重做日誌實現了原子性,在將執行SQL 語句時,會先寫入redo log buffer, 再執行 SQL 語句,若 SQL 語句執行出錯就會根據 redo log buffer 中的記錄來執行回滾操作, 由此擁有原子性。

1.2.2一致性

一致性指事務將資料庫從一種狀態轉變為下一種一致的狀態。比如有一個欄位 name 有唯一索引約束,那麼在事務前後都不能有重複的 name 出現違反唯一索引約束,否則回滾。在上述場景中即金錢總數總是 200,不能憑空增加減少。

MySQL 通過 undo Log 實現一致性, 執行 SQL 語句時,會先寫入 undo log 再寫入 redo log buffer。undo 是邏輯日誌,會根據之前的 SQL 語句進行相應回滾,比如之前是 insert 那麼回滾時會執行一個 delete,一個 update 會執行 一個相反的 update。並且除了回滾,undo log 還有一個作用是 MVCC,當使用者讀取一行記錄時,若該記錄已經被其他事務佔用,當前事務可通過 undo 讀取之前的行版本資訊,實現非鎖定讀取。並且 undo log 也會產生 redo log,因為 undo log 也需要永續性的保護。

1.2.3隔離性

首先介紹如果沒有隔離性會發生的 4 種情況丟失更新 A 事務撤銷時,把已經提交的 B 事務的更新資料覆蓋了。這種錯誤可能造成很嚴重的問題,通過下面的賬戶取款轉賬就可以看出來,MySQL 通過三級封鎖協議的第一級解決了丟失更新,事務 T 要修改資料 A 時必須加 X 鎖,直到 T 結束才釋放鎖。

髒讀

髒讀主要是讀取到了其他事務的資料,而其他事務隨後發生回滾。MySQL 通過三級封鎖協議的第二級解決了髒讀,在一級的基礎上,要求讀取資料 A 時必須加 S 鎖,讀取完馬上釋放 S 鎖。

不可重複讀

不可重複讀是讀取到資料後,隨後其他事務對資料發生了修改,無法再次讀取。MySQL 通過三級封鎖協議的第三級解決了不可重複讀。在二級的基礎上,要求讀取資料 A 時必須加 S 鎖,直到事務結束了才能釋放 S 鎖。

幻讀

幻讀是讀取到資料後,隨後其他事務對資料發生了新增,無法再次讀取。在 InnoDB 引擎Repeatable Read 的隔離級別下,MySQL 通過 Next-Key Lock 以及 MVCC 解決了幻讀,事務中分為當前讀以及快照讀。

1.2.4永續性

一旦事務提交,則其所做的修改就會永久儲存到資料庫中。此時即使系統崩潰,修改的資料也不會丟失。具體實現原理就是在事務 commit 之前會將,redo log buffer 中的資料持久化到硬碟中的 redo log file,這樣在 commit 的時候,硬碟中已經有了我們修改或新增的資料,由此做到持久化。

1.3 事務原理與鎖

1.3.1 鎖的問題場景:

多對一問題,多個操作者同時操作一個資源,而資源的狀態變化是非原子的(有中間態),哄搶會導致資源狀態混亂在這裡插入圖片描述

1.3.2 事務的問題場景:

一對多的問題,一個操作者需要繫結操作一系列資源(比如多條 sql),若任何一條操作失敗,都會導致整個操作失去意義;在這裡插入圖片描述

1.3.3 事務的實現(瞭解即可):

在這裡插入圖片描述

1. redo log

redo log 叫做重做日誌,是用來實現事務的永續性。該日誌檔案由兩部分組成:重做日誌緩衝(redo log buffer)以及重做日誌檔案(redo log),前者是在記憶體中,後者在磁碟中。當事務提交之後會把所有修改資訊都會存到該日誌中。

PS:mysql 為了提升效能不會把每次的資料修改都實時同步到磁碟,而是會先存到 Boffer Pool(緩衝池)裡頭,把這個當作快取來用。然後使用後臺執行緒去做緩衝池和磁碟之間的同步。 redo log 主要用來恢復資料, 用於保障,已提交事務的持久化特性(當機時,redo log 的資訊是全的)

2.undo log

undo log 叫做回滾日誌,用於記錄資料被修改前的資訊。他正好跟前面所說的重做日誌所記錄的相反,重做日誌記錄資料被修改後的資訊。

undo log 主要記錄的是資料的邏輯變化,為了在發生錯誤時回滾之前的操作,需要將之前的操作都記錄下來,然後在發生錯誤時才可以回滾。

PS:mysql 每次寫入資料或者修改資料之前都會把修改前的資訊記錄到 undo log undo log 是用來回滾資料,用於保障 未提交事務的原子性

3.mysql 鎖技術

當多個請求同時來臨時,mysql 要控制讀讀可並行,而寫讀,寫寫不能並行在這裡插入圖片描述

4.MVCC 基礎

MVCC (MultiVersion Concurrency Control) 叫做多版本併發控制。

InnoDB 的 MVCC ,是通過在每行記錄的後面儲存兩個隱藏的列來實現的。這兩個列,一個儲存了行的建立時間,一個儲存了行的過期時間,當然儲存的並不是實際的時間值,而是系統版本號。

他的主要實現思想是通過資料多版本來做到讀寫分離。從而實現不加鎖讀進而做到讀寫並行。

總結

  • 事務的原子性是通過 undo log 來實現的
  • 事務的永續性是通過 redo log 來實現的 -事務的隔離性是通過 (讀寫鎖+MVCC)來實現的
  • 而事務的終極大 boss 一致性是通過原子性,永續性,隔離性來實現的!!!

2.分散式事務

2.1分散式事務概念

2.1.1 分散式事務產生的原因

隨著網際網路高速發展,事務的參與者、支援事務的伺服器、資源伺服器以及事務管理器分別位於不同的分散式系統的不同節點之上。簡單的說,就是一次大的操作由不同的小操作組成,這些小的操作分佈在不同的伺服器上,且屬於不同的應用。在這種環境中,我們之前說過資料庫的 ACID 四大特性,已經無法滿足我們分散式事務。本質上來說,分散式事務就是為了保證不同資料庫的資料一致性。在這裡插入圖片描述

2.1.2 CAP 理論

CAP 定理,又被叫作布魯爾定理。CAP 指的是:一致性(Consistency)、可用性(Availability)、分割槽容錯性(Partition tolerance)。CAP 定律說的是,在一個分散式系統中,最多隻能滿足 C、A、P 中的兩個,不可能三個同時滿足。而在分散式系統中,網路無法 100% 可靠,分割槽其實是一個必然現象。

如果我們選擇了 CA 而放棄了 P,那麼當發生分割槽現象時,為了保證一致性,這個時候必須拒絕請求,但是 A 又不允許,所以分散式系統理論上不可能選擇 CA 架構,只能選擇 CP 或者 AP 架構。

而且,顯然,任何橫向擴充套件策略都要依賴於資料分割槽。因此,設計人員必須在一致性與可用性之間做出選擇。

2.1.3 BASE 理論

往往在分散式系統中無法實現完全一致性,於是有了BASE 理論,它是對 CAP 定律的進一步擴充

BASE 指的是:

  • Basically Available(基本可用) : 分散式系統在出現故障時,允許損失部分可用功能,保證核心功能可用

  • Soft state(軟狀態): 允許系統中存在中間狀態,這個狀態不影響系統可用性

  • Eventually consistent(最終一致性) : 經過一段時間後,所有節點資料都將會達到一致

BASE 理論是對 CAP 中的一致性和可用性進行一個權衡的結果。BASE 理論核心思想就是:我們無法做到強一致,但每個應用都可以根據自身的業務特點,採用適當的方式來使系統達到最終一致性

BASE 和 ACID 是相反的,它完全不同於 ACID 的強一致性模型,而是通過犧牲強一致性來獲得可用性,並允許資料在一段時間內是不一致的,但最終達到一致狀態。

2.2基於 XA 協議的兩階段提交

X/Open 組織提出了分散式事務的規範——XA 協議,XA 協議包含兩部分:事務管理器和本地資源管理器。

其中本地資源管理器往往由資料庫實現,目前主流的關係型資料庫都實現了 XA 介面, 而事務管理器作為全域性的排程者,負責各個本地資源的提交和回滾。

XA 的核心,便是全域性事務,通過XA 二階段提交協議,與各分散式資料互動,分準備與提交兩個階段邏輯流程如下圖:在這裡插入圖片描述

在 XA 協議中事務分為兩階段:

  • 事務管理器要求每個涉及到事務的資料庫預提交(precommit)此操作,並反映是否可以提交。
  • 事務協調器要求每個資料庫提交資料,或者回滾資料。

優點

  • 儘量保證了資料的強一致,實現成本較低,在各大主流資料庫都有自己實現,對於 MySQL 是從 5.5 開始支援。

缺點

  • 單點問題:事務管理器在整個流程中扮演的角色很關鍵,如果其當機,比如在第一階段已經完成,在第二階段正準備提交 的時候事務管理器當機,資源管理器就會一直阻塞,導致資料庫無法使用。
  • 同步阻塞:在準備就緒之後,資源管理器中的資源一直處於阻塞,直到提交完成,釋放資源。
  • 資料不一致:兩階段提交協議雖然為分散式資料強一致性所設計,但仍然存在資料不一致性的可能。

比如在第二階段中,假設協調者發出了事務 Commit 的通知,但是因為網路問題該通知僅被一部分參與者所收到並執行了 Commit 操作,其餘的參與者則因為沒有收到通知一直處於阻塞狀態,這時候就產生了資料的不一致性。 兩階段提交方案鎖定資源時間長,對效能影響很大,基本不適合解決微服務事務問題。

2.3 3 PC 事務

3PC,全稱 “three phase commit”,是 2PC 的改進版,其將 2PC 的 “提交事務請求” 過程一分為二。

如下圖所示在這裡插入圖片描述

2.3.1第一個階段: CanCommit

1.事務詢問:協調者向所有的參與者傳送一個包含事務內容的 canCommit 請求,詢問是否可以執行事務提交操作,並開始等待各參與者的響應。

2.各參與者向協調者反饋事務詢問的響應:參與者接收來自協調者的 canCommit 請求,如果參與者認為自己可以順利執行事務,就返回 Yes,否則反饋 No 響應。這一階段主要是確定分散式事務的參與者是否具備了完成commit 的條件,並不會執行事務操作。

2.3.2第二階段:precommit

協調者在得到所有參與者的響應之後,會根據結果執行 2 種操作:執行事務預提交,或者中斷事務。

1.執行事務預提交分為 3 個步驟

  • 傳送預提交請求:協調者向所有參與者節點發出 preCommit 的請求,並進入 prepared 狀態。

  • 事務預提交:參與者受到 preCommit 請求後,會執行事務操作,對應 2PC 中的 “執行事務”,也會 Undo 和 Redo 資訊記錄到事務日誌中。

  • 各參與者向協調者反饋事務執行的結果:如果參與者成功執行了事務,就反饋 Ack 響應,同時等待指令:提交(commit) 或終止(abor)。 2.中斷事務也分為 2 個步驟

  • 傳送中斷請求:協調者向所有參與者節點發出 abort 請求 。

  • 中斷事務:參與者如果收到 abort 請求或者超時了,都會中斷事務。

2.3.3第三階段:docommit

1.執行提交

  • 傳送提交請求:進入這一階段,如果協調者正常工作,並且接收到了所有協調者的 Ack 響應,那麼協調者將從 “預提交” 狀態變為 “提交” 狀態,並向所有的參與者傳送 doCommit 請求 。
  • 事務提交:參與者收到 doCommit 請求後,會正式執行事務提交操作,並在完成之後釋放在整個事務執行期間佔用的事務資源。
  • 反饋事務提交結果:參與者完成事務提交後,向協調者傳送 Ack 訊息。
  • 完成事務:協調者接收到所有參與者反饋的 Ack 訊息後,完成事務。 2.中斷事務(假設有任何參與者反饋了 no 響應,或者超時了,就中斷事務)
  • 傳送中斷請求:協調者向所有的參與者節點傳送 abort 請求。
  • 事務回滾:參與者接收到 abort 請求後,會利用其在二階段記錄的 undo 資訊來執行事務回滾操作,並在完成回滾之後釋放整個事務執行期間佔用的資源。
  • 反饋事務回滾結果:參與者在完成事務回滾之後,想協調者傳送 Ack 訊息。
  • 中斷事務:協調者接收到所有的 Ack 訊息後,中斷事務。

2.3.4與 2pc 的區別

注意:在階段三,可能會出現 2 種故障:協調者出現問題/協調者和參與者之間的網路故障一段出現了任一一種情況,最終都會導致參與者無法收到 doCommit 請求或者 abort 請求, 針對這種情況,參與者都會在等待超時之後,繼續進行事務提交。

優點

相比較 2PC,最大的優點是減少了參與者的阻塞範圍(第一個階段是不阻塞的),並且能夠在單點故障後繼續達成一致(2PC 在提交階段會出現此問題,而 3PC 會根據協調者的狀態進行回滾或者提交)。

缺點

如果參與者收到了 preCommit 訊息後,出現了網路分割槽,那麼參與者等待超時後,都會進行事務的提交,這必然會出現事務不一致的問題。

2.4 TCC 方案

TCC 其實就是採用的補償機制,其核心思想是:針對每個操作,都要註冊一個與其業務邏輯對應的確認和補償(撤銷)操作。

其將整個業務邏輯的每個分支顯式的分成了 Try、Confirm、Cancel 三個操作。Try 部分完成業務的準備工作,confirm 部分完成業務的提交,cancel 部分完成事務的回滾。

在這裡插入圖片描述

優點:跟 2PC 比起來,實現以及流程相對簡單了一些,但資料的一致性比 2PC 也要差一些

缺點:TCC 屬於應用層的一種補償方式,所以需要程式設計師在實現的時候多寫很多補償的程式碼,而且補償的時候也有可能失敗,在一些場景中,一些業務流程可能用TCC 不太好定義及處理。

2.5 MQ(事務訊息)

目前,僅阿里雲的RocketMQ 支援事務訊息。幫助使用者實現類似 X/Open XA 的分佈事務功能,通過 MQ 事務訊息能達到分散式事務的最終一致。

在這裡插入圖片描述

說明

  1. 傳送方向 MQ 服務端傳送訊息

  2. MQ Server 將訊息持久化成功之後,向傳送方 ACK 確認訊息已經傳送成功,此時訊息為半訊息

  3. 傳送方開始執行本地事務邏輯

  4. 傳送方根據本地事務執行結果向 MQ Server 提交二次確認(Commit 或是 Rollback),MQ Server 收到 Commit 狀態則將半訊息標記為可投遞,訂閱方最終將收到該訊息;MQ Server 收到 Rollback 狀態則刪除半訊息,訂閱方將不會接受該訊息

  5. 在斷網或者是應用重啟的特殊情況下,上述步驟 4 提交的二次確認最終未到達 MQ Server,經過固定時間後 MQ Server 將對該訊息發起訊息回查

  6. 傳送方收到訊息回查後,需要檢查對應訊息的本地事務執行的最終結果

  7. 傳送方根據檢查得到的本地事務的最終狀態再次提交二次確認,MQ Server 仍按照步驟 4 對半訊息進行操作

其中,事務訊息傳送對應步驟 1、2、3、4,事務訊息回查對應步驟 5、6、7

2.6 Lcn 事務

2.6.1 背景

LCN 名稱是由早期版本的 LCN 框架命名,在設計框架之初的 1.0 ~ 2.0 的版本時框架設計的步驟是如下,各取其首字母得來的LCN 命名。

  • 鎖定事務單元(lock)
  • 確認事務模組狀態(confirm)
  • 通知事務(notify)

2.6.2框架定位

LCN 並不生產事務,LCN 只是本地事務的協調工,TX-LCN 定位於一款事務協調性框架,框架其本身並不操作事務,而是基於對事務的協調從而達到事務一致性的效果

2.6.3事務控制原理

TX-LCN 由兩大模組組成, TxClient、TxManager

  • TxClient 作為模組的依賴框架,提供 TX-LCN 的標準支援
  • TxManager 作為分散式事務的控制方。事務發起方或者參與反都由TxClient 端來控制。

原理圖

在這裡插入圖片描述

  • 1.建立事務組:是指在事務發起方開始執行業務程式碼之前先呼叫 TxManager 建立事務組物件,然後拿到事務標示 GroupId 的過程。
  • 2.加入事務組:新增事務組是指參與方在執行完業務方法以後,將該模組的事務資訊通知給 TxManager 的操作。
  • 3.通知事務組:是指在發起方執行完業務程式碼以後,將發起方執行結果狀態通知給 TxManager,TxManager 將根據事務最終狀態和事務組的資訊來通知相應的參與模組提交或回滾事務,並返回結果給事務發起方。

2.7Seata 事務

2.7.1背景

Seata(Simple Extensible Autonomous Transaction Architecture) 是 阿里巴巴開源的分散式事務中介軟體,以高效並且對業務 0 侵入的方式,解決微服務場景下面臨的分散式事務問題。

2.7.2設計思想

seata 的 AT 模式,採用的是大量運用在資料庫軟體的 Write Ahead Log 思想,即把事務的資訊以事務日誌的方式記錄下來。這種處理方式,實際上是對傳統兩階段提交的一種改進和優化。主要有幾個關鍵點

  1. 傳統兩階段提交協議是阻塞協議,效能差
  2. 傳統兩階段提交協議高可用性不好
  3. 傳統兩階段提交協議的全域性事務隔離機制不支援
  4. 根據八二原則,80% 的涉及到全域性事務的業務是能正常完成並提交的。

因此,在 AT 模式下,seata 採取的做法是,一個事務分支的資料庫操作執行完後,馬上進行本地事務的提交,從而釋放相關的資料庫資源

在這裡插入圖片描述

  • 分支事務中資料的 本地鎖 由本地事務管理,在分支事務 Phase1 結束時釋放。
  • 同時,隨著本地事務結束,連線 也得以釋放。
  • 分支事務中資料的 全域性鎖 在事務協調器側管理,在決議 Phase2 全域性提交時,全域性鎖馬上可以釋放。只有在決議全域性回滾的情況下,全域性鎖 才被持有至分支的 Phase2 結束。

2.7.3 本地事務執行流程

在進行本地提交的前提是,seata 會解析 SQL,獲取資料庫表的後設資料,根絕 SQL 型別,選擇性地生成資料的前置映象和後置映象, 儲存在 undolog 表中,並且要求與儲存 undolog 與業務 SQL 在同一個本地事務內。

這就保證了

  1. 如果一個本地事務被提交,那麼必定對應著相應的 undo_log
  2. 如果儲存 undo_log 儲存失敗,那麼業務 SQL 也會失敗

在這裡插入圖片描述

2.7.4全域性事務提交流程

因為每個分支事務的本地事務都已經被提交,所以如果全域性事務能夠順利進行到“提交“這一階段,那麼意味著所有事務分支的本地事務都已經被提交了,資料的一致性已經得到了保證

這個時候全域性事務的提交就變得十分輕量級,就是把 undo_log 對應的記錄刪掉即可,即使是當時刪除失敗了,也已經不會影響全域性事務的最終結果,這次刪不了,那就待會再刪,程式刪不了,沒事,頂多人工刪。

在這裡插入圖片描述

2.7.5全域性事務回滾流程

如果全域性事務的任何一個事務分支失敗了,那麼全域性事務就進入“回滾“流程,回滾時依據先前儲存好資料映象,將原來的資料回放回去

最後

java高階開發系統進階筆記、最新面試複習筆記PDF,我的GitHub

相關文章