在上篇文章 MySQL 事務的隔離級別 中已經提到了事務的特性、事務的隔離級別及各個隔離級別可能導致的問題,下面來說說MySQL中事務的使用
-
MySQL 事務簡單使用
# 查看事務自動提交的模式 show [session] variables like 'autocommit'; //會話級別 show global variables like 'autocommit'; // 全局級別 # 關閉自動提交: set global autocommit=0; set [session] autocommit=0; # 開啟自動提交: set global autocommit=1; set [session] autocommit=1; # 開啟事務 begin; 或: start transaction; # 提交 commit; # 回滾 rollback;
在代碼里使用事務前
- 需要先關閉自動提交
- 然后開啟事務,
- 執行操作數據的SQL
- 如果中間SQL語句拋異常了,就調rollback 回滾,將數據恢復到事務開始前的狀態,事務結束
- 如果中間SQL語句沒有拋異常,就commit 提交,事務結束
-
MySQL 分布式事務 參考:MySQL 中基於 XA 實現的分布式事務
對於有關聯的數據的處理有時候可能不在一個數據庫中,這個時候如果想要保證數據絕對可靠,就需要考慮使用分布式事務了。
-
XA協議
-
XA規范中分布式事務有AP,RM,TM組成:
- 其中應用程序(Application Program ,簡稱AP):AP定義事務邊界(定義事務開始和結束)並訪問事務邊界內的資源。
- 資源管理器(Resource Manager,簡稱RM):Rm管理計算機共享的資源,許多軟件都可以去訪問這些資源,資源包含比如數據庫、文件系統、打印機服務器等。
- 事務管理器(Transaction Manager ,簡稱TM):負責管理全局事務,分配事務唯一標識,監控事務的執行進度,並負責事務的提交、回滾、失敗恢復等。
-
Xa主要規定了RM與TM之間的交互,XA規范中定義的RM 和 TM交互的接口:
-
xa_start負責開啟或者恢復一個事務分支,並且管理XID到調用線程
-
xa_end 負責取消當前線程與事務分支的關聯
-
xa_prepare負責詢問RM 是否准備好了提交事務分支
-
xa_commit通知RM提交事務分支
-
xa_rollback 通知RM回滾事務分支
-
-
XA協議使用了二階段協議
- 第一階段TM要求所有的RM准備提交對應的事務分支,詢問RM是否有能力保證成功的提交事務分支,RM根據自己的情況,如果判斷自己進行的工作可以被提交,那就對工作內容進行持久化,並給TM回執OK;否者給TM的回執NO。RM在發送了否定答復並回滾了已經的工作后,就可以丟棄這個事務分支信息了。
- 第二階段TM根據階段1各個RM prepare的結果,決定是提交還是回滾事務。如果所有的RM都prepare成功,那么TM通知所有的RM進行提交;如果有RM prepare回執NO的話,則TM通知所有RM回滾自己的事務分支。
-
-
MySQL中XA實現
-
通過 show engines; 可以看到 只有InnoDB存儲引擎支持XA事務
-
在MySQL數據庫分布式事務中,MySQL是XA事務過程中的資源管理器(RM)存在的,TM是連接MySQL服務器的客戶端。MySQL數據庫是作為RM存在的,在分布式事務中一般會涉及到至少兩個RM。需要注意的是MySQL中只有當隔離級別為Serializable時候才能使用分布式事務,所以需要使用
set global tx_isolation='serializable';
設置數據庫隔離級別 -
MySQL 中使用XA
show variables like ‘innodb_support_xa’; //查看是否開啟了 XA 功能 XA {START|BEGIN} xid [JOIN|RESUME] //開啟XA事務,如果使用的是XA START而不是XA BEGIN,那么不支持[JOIN|RESUME],xid是一個唯一值,表示事務分支標識符 XA END xid [SUSPEND [FOR MIGRATE]] //結束一個XA事務,不支持[SUSPEND [FOR MIGRATE]] XA PREPARE xid 准備提交 XA COMMIT xid [ONE PHASE] //提交,如果使用了ONE PHASE,則表示使用一階段提交。兩階段提交協議中,如果只有一個RM參與,那么可以優化為一階段提交 XA ROLLBACK xid //回滾 XA RECOVER [CONVERT XID] //列出所有處於PREPARE階段的XA事務
-
其中首先使用XA START ‘xid' 啟動了一個XA事務,並把它置於ACTIVE狀態
-
對於一個ACTIVE狀態的 XA事務,我們可以執行構成事務的多條SQL語句,也就是指定分支事務的邊界,然后執行一個XA END ‘xid'語句,XA END把事務放入IDLE狀態,也就是結束事務邊界,在xa start和xa end之間的語句就構成了本分支事務的一個事務范圍。當調用xa end 'xid1'后由於結束了事務邊界,所以這時候如果再執行sql語句會拋出ERROR 1399 (XAE07): XAER_RMFAIL: The command cannot be executed when global transaction is in the IDLE state錯誤,也就是當分支事務處於IDLE狀態時候不允許執行沒有包含到分支事務邊界里面的其他SQL.
-
對於一個IDLE 狀態XA事務,可以執行一個XA PREPARE語句或一個XA COMMIT…ONE PHASE語句,其中XA PREPARE把事務放入PREPARED狀態。在此點上的XA RECOVER語句將在其輸出中包括事務的xid值,因為XA RECOVER會列出處於PREPARED狀態的所有XA事務。XA COMMIT…ONE PHASE用於預備和提交事務,也就是轉換為一階段協議,直接提交事務。
-
對於一個PREPARED狀態的 XA事務,可以執行XA COMMIT 語句來提交或者執行XA ROLLBACK來回滾xa事務。
-
-
-
分布式事務的幾個解決方案 參考:對於 MySQL 分布式事務的看法
-
TCC補償模式
TCC 方案是二階段提交的 另一種實現方式,它涉及 3 個模塊,主業務、從業務和 活動管理器(協作者)
-
第一階段:主業務服務分別調用所有從業務服務的 Try 操作,並在活動管理器中記錄所有從業務服務。當所有從業務服務 Try 成功或者某個從業務服務 Try 失敗時,進入第二階段。
-
第二階段:活動管理器根據第一階段從業務服務的 Try 結果來執行 Confirm 或 Cancel 操作。如果第一階段所有從業務服務都 Try 成功,則協作者調用所有從業務服務的 Confirm 操作,否則,調用所有從業務服務的 cancel 操作。
-
在第二階段中,Confirm 和 Cancel 同樣存在失敗情況,所以需要對這兩種情況做異常處理以保證數據一致性。
-
Confirm 失敗:則回滾所有 Confirm 操作並執行 Cancel 操作。Cancel 失敗:從業務服務需要提供自動重試 Cancel 機制,以保證 Cancel 成功。
注:這種方案實現的要素在於,調用鏈需要被記錄,且每個服務提供者都需要提供一組業務邏輯相反的操作,互為補償,同時回滾操作和確認提交操作要保證冪等
-
-
消息隊列可靠消息提交
利用消息隊列,需要執行的操作的時候 產生一條可靠的消息到消息隊列,然后可靠地消費這個消息。通過 消息隊列的消息完成最終的一致性
以轉賬服務為例,當支付寶賬戶扣除1萬后,我們只要生成一個憑證(消息)即可,這個憑證(消息)上寫着“讓余額寶賬戶增加 1萬”,只要這個憑證(消息)能可靠保存,我們最終是可以拿着這個憑證(消息)讓余額寶賬戶增加1萬的,即我們能依靠這個憑證(消息)完成最終一致性。最為核心的問題便是如何可靠的保證消息會被消費以及如何解決消息重復投遞的問題。
-
如何可靠保存憑證(消息):
1)支付寶在扣款事務提交之前,向實時消息服務請求發送消息,實時消息服務只記錄消息數據,而不真正發送,只有消息發送成功后才會提交事務; ( 相當於先准備了一個消息,但是不能被消費)
2)當支付寶扣款事務被提交成功后,向實時消息服務確認發送。只有在得到確認發送指令后,實時消息服務才真正發送該消息;
3)當支付寶扣款事務提交失敗回滾后,向實時消息服務取消發送。在得到取消發送指令后,該消息將不會被發送;
4)對於那些未確認的消息或者取消的消息,需要有一個定時任務 定時去支付寶系統查詢這個消息的狀態並進行更新。為什么需要這一步驟,舉個例子:假設在第2步支付寶扣款事務被成功提交后,系統掛了,此時消息狀態並未被更新為“確認發送”,從而導致消息不能被發送。
優點:消息數據獨立存儲,降低業務系統與消息系統間的耦合;
缺點:一次消息發送需要兩次請求;業務處理服務需要實現消息狀態回查接口。 -
如何解決消息重復投遞的問題:
還有一個很嚴重的問題就是消息重復投遞,以我們支付寶轉賬到余額寶為例,如果相同的消息被重復投遞兩次,那么我們余額寶賬戶將會增加2萬而不是1萬了。
為什么相同的消息會被重復投遞?比如余額寶處理完消息msg后,發送了處理成功的消息給實時消息服務,正常情況下實時消息服務應該要刪除消息msg,但如果實時消息服務這時候悲劇的掛了,重啟后一看消息msg還在,就會繼續發送消息msg。
解決方法很簡單,消費消息做到冪等性,在余額寶這邊增加消息應用狀態表(message_apply),通俗來說就是個賬本,用於記錄消息的消費情況,每次來一個消息,在真正執行之前,先去消息應用狀態表中查詢一遍,如果找到說明是重復消息,丟棄即可,如果沒找到才執行,同時插入到消息應用狀態表(同一事務)。
-
-
阿里開源分布式中間件 Seata —— 標准分布式模型 TXC
-
Seata 內部定義了 3個模塊來處理全局事務和分支事務的關系和處理過程,這三個組件分別是:
Transaction Coordinator (TC):事務協調器,維護全局事務的運行狀態,負責協調並驅動全局事務的提交或回滾。
Transaction Manager (TM):事務的發起者,控制全局事務的邊界,負責開啟一個全局事務,並最終發起全局提交或全局回滾的決議。
Resource Manager (RM):負責控制每個服務的分支事務,負責分支注冊、狀態匯報,並接收事務協調器的指令,驅動分支(本地)事務的提交和回滾。 -
簡要說說整個全局事務的執行步驟:
TM 向 TC 申請開啟一個全局事務,TC 創建全局事務后返回全局唯一的 XID,XID 會在全局事務的上下文中傳播;
RM 向 TC 注冊分支事務,該分支事務歸屬於擁有相同 XID 的全局事務;
TM 向 TC 發起全局提交或回滾;
TC 調度 XID 下的分支事務完成提交或者回滾。 -
相對於XA ,Seata 優化了鎖定的機制。RM 到 TC 控制器端查詢操作的本地數據這一行是否被全局鎖定了,如果被鎖定了,就重新嘗試,如果沒被鎖定,則加全局鎖后開始解析 SQL,把業務數據在更新前后的數據鏡像組織成回滾日志,並將 undo log 日志插入 undo_log 表中,保證每條更新數據的業務 sql 都有對應的回滾日志存在。這樣做的好處就是,本地事務執行完可以立即釋放本地事務鎖定的資源,然后向 TC 上報分支狀態。當 TM 決議全局提交時,就不需要同步協調處理了,TC 會異步調度各個 RM 分支事務刪除對應的 undo log 日志和全局鎖,這個步驟非常快速地可以完成;當 TM 決議全局回滾時,RM 收到 TC 發送的回滾請求,RM 通過 XID 找到對應的 undo log 回滾日志,然后執行回滾日志完成回滾操作。
分支事務中數據的 本地鎖 由本地事務管理,在分支事務 Phase1 結束時釋放。同時,隨着本地事務結束,連接也得以釋放。
分支事務中數據的 全局鎖 在事務協調器側 TC 管理,在決議 Phase2 全局提交時,全局鎖馬上可以釋放。只有在決議全局回滾的情況下,全局鎖才被持有至分支的 Phase2 結束。
這個設計,極大地減少了分支事務對資源(數據和連接)的鎖定時間,給整體並發和吞吐的提升提供了基礎。
-
-
-
注:本文參考多篇文章,可以作為一個了解的知識(奈何我水平太低,后面的內容我暫時還沒能完全參透)。
本文整理不易,如需轉載請注明出處, https://www.cnblogs.com/zhuchenglin/p/12716882.html