前言
事務簡介
事務(Transaction)是訪問並可能更新資料庫中各種資料項的一個程式執行單元(unit)。 在關聯式資料庫中,一個事務由一組SQL語句組成。 事務應該具有4個屬性:原子性、一致性、隔離性、永續性。這四個屬性通常稱為ACID特性。
- 原子性(atomicity):事務是一個不可分割的工作單位, 事務中包括的諸操作要麼都做,要麼都不做。
- 一致性(consistency):事務必須是使資料庫從一個一致性狀態變到另一 個一 致性狀態, 事務的中間狀態不能被觀察到的。
- 隔離性(isolation) : 一個事務的執行不能被其他事務干擾。即一個事務內部的操作及使用的資料對併發的其他事務是隔離的,併發執行的各個事務之間不能互相干擾。隔離性又分為四個級別:讀未提交(read uncommitted)、讀已提交(read committed,解決髒讀)、可重複讀(repeatable read,解決虛讀)、序列化(serializable,解決幻讀)。
- 永續性(durability) :永續性也稱永久性(permanence) ,指一個事務一旦提交, 它對資料庫中資料的改變就應該是永久性的。接下來的其他操作或故障不應該對其有任何影響。任何事務機制在實現時,都應該考慮事務的ACID特性,包括:本地事務、分散式事務,及時不能都很好的滿足,也要考慮支援到什麼程度。
本地事務
@Transactional
,大多場景下,我們的應用都只需要操作單一的資料庫,這種情況下的事務稱之為本地事務(Local Transaction),本地事務的 ACID 特性是資料庫直接提供支援。本地事務應用架構如下
在 JDBC 程式設計中,我們透過java.sql.Connection
物件來開啟、關閉或者提交事務
Connection conn = ... // 獲取資料庫連線
conn.setAutoCommit(false);// 關閉自動提交
try{
// ... 執行增刪改查 sql
conn.commit(); // 提交事務
} catch(Exception e) {
conn.rollback(); // 事務回滾
} finally {
conn.close(); //關閉連線
}
分散式事務
下面兩種情況如果是按照之前的本地事務,是無法保證事務的。
一、什麼是 Seata
Seata是一款開源的分散式事務解決方案, 致力於提供高效能和簡單易用的分散式事務服務。Seata 將為使用者提供了AT、TCC、SAGA 和 XA 事務模式,為使用者打造一站式的分散式解決方案。AT 模式是阿里首推的模式,阿里雲上有商用版本的 GTS (Global Transaction Service全域性事務服務)
常見的分散式事務解決方案
- seata 阿里分散式事務框架
- 訊息佇列
- SAGA
- XA
他們有一個共同點,都是“兩階段(2PC)”。“兩階段”是指完成整個分 布式事務,劃分成兩個步驟完成。
實際上,這四種常見的分散式事務解決方案,分別對應著分散式事務的四種模式: AT、 TCC、 Saga、 XA;
2PC兩階段提交(Two-Phase Commit)協議:
顧名思義,分為兩個階段:Prepare 和 Commit
1.1、Prepare:提交事務請求
基本流程如下
- 詢問協調者向所有參與者傳送事務請求,詢問是否可執行事務操作,然後等待各個參與者的響應。
- 執行各個參與者接收到協調者事務請求後,執行事務操作(例如更新一個關 系型資料庫表中的記錄),並將 Undo 和 Redo 資訊記錄事務日誌中。
- 響應如果參與者成功執行了事務並寫入Undo和Redo資訊,則向協調者返回YES響應,否則返回NO響應。當然,參與者也可能當機,從而不會返回響應。
1.2、正常提交事務
- commit請求協調者向所有參與者傳送Commit請求。
- 事務提交參與者收到 Commit 請求後,執行事務提交,提交完成後釋放事務執行期佔用的所有資源。
- 反饋結果參與者執行事務提交後向協調者傳送Ack響應。
- 完成事務接收到所有參與者的Ack響應後,完成事務提交。
1.3、中斷事務
在執行Prepare步驟過程中,如果某些參與者執行事務失敗、當機或與協調者之間的網路中斷,那麼協調者就無法收到所有參與者的 YES 響應,或者某個參與者返回了 No 響應,此時,協調者就會進入回退流程,對事務進行回退。流程如下圖紅色部分(將Commit請求替換為紅色的Rollback 請求)
- rollback請求協調者向所有參與者傳送Rollback請求。
- 事務回滾參與者收到Rollback後,使用Prepare階段的Undo日誌執行事務回滾,完成後釋放事務執行期佔用的所有資源。
- 反饋結果參與者執行事務回滾後向協調者傳送 Ack 響應。
- 中斷事務接收到所有參與者的Ack響應後,完成事務中斷。
二、2PC 的問題
- 同步阻塞參與者在等待協調者的指令時,其實是在等待其他參與者的響應,在此過程中,參與者是無法進行其他操作的,也就是阻塞了其執行。倘若參 與者與協調者之間網路異常導致參與者一直收不到協調者資訊,那麼會導致參與者一直阻塞下去。
- 單點在2PC中,一切請求都來自協調者,所以協調者的地位是至關重要的,如果協調者當機,那麼就會使參與者一直阻塞並一直佔用事務資源。如果協調者也是分散式,使用選主方式提供服務,那麼在一個協調者掛掉後, 可以選取另一個協調者繼續後續的服務,可以解決單點問題。但是,新協調者無法知道上一個事務的全部狀態資訊(例如已等待Prepare響應的時長等),所以也無法順利處理上一個事務。
- 資料不一致Commit事務過程中Commit請求/Rollback請求可能因為協調者當機或協調者與參與者網路問題丟失,那麼就導致了部分參與者沒有收到Commit/Rollback請求,而其他參與者則正常收到執行了CommitRollback操作,沒有收到請求的參與者則繼續阻塞。這時,參與者之間的資料就不再一 致了。當參與者執行CommitRollback後會向協調者傳送Ack,然而協調者不論是否收到所有的參與者的Ack,該事務也不會再有其他補救措施了,協調者能做的也就是等待超時後像事務發起者返回一個“我不確定該事務是否成功”。
- 環境可靠性依賴協調者Prepare請求發出後,等待響應,然而如果有參與者當機或與協調者之間的網路中斷,都會導致協調者無法收到所有參與者的響應,那麼在 2PC 中,協調者會等待一定時間, 然後超時後,會觸發事務中斷,在這個過程中,協調者和所有其他參與者都是出於阻塞的。這種機制對網路問題常見的現實環境來說太苛刻了。
三、AT 模式(auto transaction)
AT模式是一種無侵入的分散式事務解決方案。
阿里seata框架,實現了該模式。
在AT模式下,使用者只需關注自己的“業務SQL”,使用者的“業務SQL”作為一階段, Seata 框架會自動生成務的二階段提交和回滾操作。
- 一階段
在一階段,Seata 會攔截“業務SQL”,首先解析 SQL 語義,找到“業務 SQL”要更新的業務資料,在業務資料被更新前,將其儲存成“before image”,然後執行“業務SQL”更新業務資料,在業務資料更新之後,再將其儲存成”after image”,最後生成行鎖。以上操作全部在一個資料庫事務內完成, 這樣保證了一階段操作的原子性。
- 二階段提交
二階段如果是提交的話,因為“業務SQL”在一階段已經提交至資料庫,所以 Seata 框架只需將一階段儲存的快照資料和行鎖刪掉,完成資料清理即可。
- 二階段回滾
二階段如果是回滾的話,Seata 就需要回滾一階段已經執行的“業務SQL”,還原業務資料。回滾方式便是用“before image”還原業務資料;但在還原前要首先要校驗髒寫,對比“資料庫當前業務資料”和“after image”,如果兩份資料完全一致就說明沒有髒寫, 可以還原業務資料,如果不一致就說明有髒寫,出現髒寫就需要轉人工處理。
四、TCC 模式
TCC 模式需要使用者根據自己的業務場景實現 Try、Confirm 和 Cancel 三個操作;事務發起方在一階段執行 Try 方式,在二階段提交執行 Confim 方法,二階段回滾執行 Cancel 方法。
缺點:
侵入性強,並且得自己實現相關事務控制邏輯
優點:
在整個過程中基本沒有鎖,效能更強
五、基於訊息佇列實現分散式事務
六、Seata 詳細說明
在Seata的架構中,一共有三個角色:
TC (Transaction Coordinator) - 事務協調者
維護全域性和分支事務的狀態,驅動全域性事務提交或回滾。
TM (Transaction Manager) - 事務管理器
定義全域性事務的範圍:開始全域性事務、提交或回滾全域性事務。
RM (Resource Manager) - 資源管理器
管理分支事務處理的資源,與TC交談以註冊分支事務和報告分支事務的狀態,並驅動分支事務提交或回滾。
其中,TC為單獨部署的Server服務端,TM和RM為嵌入到應用中的Client客戶端。
在 Seata 中,一個分散式事務的生命週期如下
1.TM 請求 TC 開啟一個全域性事務。TC 會生成一個 XID 作為該全域性事務的編號。XID 會在微服務的呼叫鏈路中傳播,保證將多個微服務的子事務關聯在一起。
2.RM 請求 TC 將本地事務註冊為全域性事務的分支事務,透過全域性事務的XID進行關聯。
3.TM 請求 TC 告訴 XID 對應的全域性事務是進行提交還是回滾。
4.TC 驅動 RM 們將 XID 對應的自己的本地事務進行提交還是回滾。
本作品採用《CC 協議》,轉載必須註明作者和本文連結