分佈式事務詳解(mysql事務如何實現的,分佈式事務解決方案)

分佈式事務也是面試的一個高頻知識點。

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執行的過程中,儘量規避中間狀態。即謂事務。事務在解決上述問題中,提出了以下四種特性

原子性(Atomicity),一致性(Consistency),隔離性(Isolation),持久性(Durability)

1.2.1 原子性

原子性就是不可拆分的特性,要麼全部成功然後提交(commit),要麼全部失敗然後回滾(rollback)。若開啟事務,在上述場景就不會出現 A 少 100 成功,B 多 100 失敗 這種情況。利用InnoDB的undo logundo log(回滾日誌)記錄需要回滾的日誌信息,是實現原子性的關鍵,當事務回滾時能夠撤銷所有已經成功執行的sql語句

例如:

delete一條數據的時候,就會記錄這條數據的曾經的信息,回滾的時候,insert這條舊數據

update一條數據的時候,就會記錄之前的舊值,回滾的時候,根據舊值執行update操作

insert一條數據的時候,就會這條記錄的主鍵,回滾的時候,根據主鍵執行delete操作

undo log記錄了這些回滾需要的信息,當事務執行失敗或調用了rollback,導致事務需要回滾,便可以利用undo log中的信息將數據回滾到修改之前的樣子。

1.2.2 一致性

數據庫通過原子性(A)、隔離性(I)、持久性(D)來保證一致性(C)。其中一致性是目的,原子性、隔離性、持久性是手段。因此數據庫必須實現AID三大特性才有可能實現一致性。

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 解決了幻讀,事務中分為當前讀以及快照讀。

MVCC(Multi Version Concurrency Control)即多版本併發控制,一個行記錄數據有多個版本對快照數據,這些快照數據在undo log中。如果一個事務讀取的行正在做DELELE或者UPDATE操作,讀取操作不會等行上的鎖釋放,而是讀取該行的快照版本。

由於MVCC機制在可重複讀(Repeateable Read)和讀已提交(Read Commited)的MVCC表現形式不同在後續文章中將進行詳細描述。

在事務隔離級別為讀已提交(Read Commited)時,一個事務能夠讀到另一個事務已經提交的數據,是不滿足隔離性的。但是當事務隔離級別為可重複讀(Repeateable Read)中,是滿足隔離性的。

1.2.4 持久性

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

Mysql利用Innodb的redo log做到持久化

Mysql修改數據的大概流程是先把磁盤上的數據加載到內存中,在內存中對數據進行修改,再刷回磁盤上。如果此時突然宕機,內存中的數據就會丟失。如何解決該問題

最直觀的想法,事務提交前直接把數據寫入磁盤

這麼做有什麼問題

浪費資源,只修改一個頁面裡的一個字節,就要將整個頁面刷入磁盤(一個頁面16kb,每次改動都需要將16kb的內容刷入磁盤)

速度慢,每個事務裡可能涉及到多個數據頁的修改,而這些數據可能是不相鄰的,屬於隨機操作IO

於是,決定採用redo log解決上面的問題。當做數據修改的時候,不僅在內存中操作,還會在redo log中記錄這次操作。當事務提交的時候,會將redo log日誌進行刷盤(redo log一部分在內存中,一部分在磁盤上)。當數據庫宕機重啟的時候,會將redo log中的內容恢復到數據庫中,再根據undo log和binlog內容決定回滾數據還是提交數據。

採用redo log的優點

redo log進行刷盤的效率要遠高於數據頁刷盤,具體表現如下

redo log體積小,只記錄了哪一頁修改的內容,因此體積小,刷盤快

redo log是一直往末尾進行追加,屬於順序IO。效率顯然比隨機IO來的快

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 一致性是通過原子性,持久性,隔離性來實現的!!!

1.4 事務的操作過程

1.編程式事務:使用 TransactionTemplate 或者直接使用底層的 TransactionManager 來操作事務 commit 或者 rollback。

2.聲明式事務:建立在 AOP 基礎上,通過對方法前後進行攔截,加入編程式事務裡的流程控制邏輯。使用的時候只需要在方法前面加上@Transactional 註解

2 分佈式事務

2.1 分佈式事務概念

2.1.1 分佈式事務產生的原因

隨著互聯網高速發展,事務的參與者、支持事務的服務器、資源服務器以及事務管理器分別位於不同的分佈式系統的不同節點之上。簡單的說,就是一次大的操作由不同的小操作組成,這些小的操作分佈在不同的服務器上,且屬於不同的應用。在這種環境中,我們之前說過數據庫的 ACID 四大特性,已經無法滿足我們分佈式事務。

本質上來說,分佈式事務就是為了保證不同數據庫的數據一致性。

分佈式事務詳解(mysql事務如何實現的,分佈式事務解決方案)

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 二階段提交協議,與各分佈式數據交互,分準備與提交兩個階段

邏輯流程如下圖:

分佈式事務詳解(mysql事務如何實現的,分佈式事務解決方案)


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

· 事務管理器要求每個涉及到事務的數據庫預提交(precommit)此操作,並反映是否可以提交。

· 事務協調器要求每個數據庫提交數據,或者回滾數據。

優點:

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

缺點

· 單點問題:事務管理器在整個流程中扮演的角色很關鍵,如果其宕機,比如在第一階段已經完成,在第二階段正準備提交的時候事務管理器宕機,資源管理器就會一直阻塞,導致數據庫無法使用。

· 同步阻塞:在準備就緒之後,資源管理器中的資源一直處於阻塞,直到提交完成,釋放資源。

· 數據不一致:兩階段提交協議雖然為分佈式數據強一致性所設計,但仍然存在數據不一致性的可能。比如在第二階段中,假設協調者發出了事務 Commit 的通知,但是因為網絡問題該通知僅被一部分參與者所收到並執行了 Commit 操作,其餘的參與者則因為沒有收到通知一直處於阻塞狀態,這時候就產生了數據的不一致性。

兩階段提交方案鎖定資源時間長,對性能影響很大,基本不適合解決微服務事務問題。

2.3 3PC 事務

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

分佈式事務詳解(mysql事務如何實現的,分佈式事務解決方案)


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部分完成事務的回滾。

分佈式事務詳解(mysql事務如何實現的,分佈式事務解決方案)


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

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

2.5 MQ (事務消息)

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

分佈式事務詳解(mysql事務如何實現的,分佈式事務解決方案)

流程說明:

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 端來控制。

原理圖:

分佈式事務詳解(mysql事務如何實現的,分佈式事務解決方案)

· 1.創建事務組

是指在事務發起方開始執行業務代碼之前先調用 TxManager 創建事務組對象,然後拿到事務標示 GroupId 的過程。

· 2.加入事務組

添加事務組是指參與方在執行完業務方法以後,將該模塊的事務信息通知給 TxManager 的操作。

· 3.通知事務組

是指在發起方執行完業務代碼以後,將發起方執行結果狀態通知給 TxManager,TxManager 將根據事務最終狀態和事務組的信息來通知相應的參與模塊提交或回滾事務,並返回結果給事務發起方。

2.7 Seata 事務

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 採取的做法是,一個事務分支的數據庫操作執行完後,馬上進行本地事務的提交,從而釋放相關的數據庫資源。

分佈式事務詳解(mysql事務如何實現的,分佈式事務解決方案)

·分支事務中數據的 本地鎖 由本地事務管理,在分支事務 Phase1 結束時釋放。


·同時,隨著本地事務結束, 連接 也得以釋放。

·分支事務中數據的 鎖 全局鎖 在事務協調器側管理,在決議 Phase2 全局提交時,全局鎖馬上可以釋放。只有在決議全局回滾的情況下,鎖 全局鎖 才被持有至分支的 Phase2 結束。

2.7.3 本地事務執行流程

在進行本地提交的前提是,seata 會解析 SQL,獲取數據庫表的元數據,根絕 SQL 類型,選擇性地生成數據的前置鏡像和後置鏡像,保存在 undo_log 表中,並且要求與保存 undo_log 與業務 SQL 在同一個本地事務內。

這就保證了:

1. 如果一個本地事務被提交,那麼必定對應著相應的 undo_log

2. 如果保存 undo_log 保存失敗,那麼業務 SQL 也會失敗

分佈式事務詳解(mysql事務如何實現的,分佈式事務解決方案)

2.7.4 全局事務提交流程

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

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

分佈式事務詳解(mysql事務如何實現的,分佈式事務解決方案)

2.7.5 全局事務回滾流程

如果全局事務的任何一個事務分支失敗了,那麼全局事務就進入“回滾“流程,回滾時依據先前保存好數據鏡像,將原來的數據回放回去。

如果全局回放成功,那麼數據的一致性也就得到了保證,如果回放不成功,那麼事務就進入異常。應對異常,可能需要重試,也可能需要人工介入。

分佈式事務詳解(mysql事務如何實現的,分佈式事務解決方案)


【謝謝你的關注】


分享到:


相關文章: