DTP模型之一:(XA協議之三)MySQL數據庫分布式事務XA優缺點與改進方案


1 MySQL 外部XA分析

  1.1 作用分析

  MySQL數據庫外部XA可以用在分布式數據庫代理層,實現對MySQL數據庫的分布式事務支持,例如開源的代理工具:ameoba[4],網易的DDB,淘寶的TDDL,B2B的Cobar等等。

  通過MySQL數據庫外部XA,這些工具可以提供跨庫的分布式事務。當然,這些工具也就成了外部XA事務的協調者角色。在crash recover時控制懸掛事務是全局commit,或者rollback。

  在crash recover之后,外部應用程序可能會遇到以下幾種情況:

  情況一:分布式事務對應的MySQL數據庫實例,部分完成prepare,部分未完成prepare。此時直接回滾完成prepare的實例即可。n_prepared < Total Nodes (處於prepare狀態的節點數量要小於參與分布式事務的所有節點總數)。

  情況二:分布式事務對應的MySQL實例,全部完成prepare,未開始進行commit。此時即可提交此事務,也可回滾此事務(根據分布式事務原理,所有節點都完成prepare,應該提交)。n_prepared = Total Nodes。

  情況三:分布式事務對應的MySQL實例,全部完成prepare,並且部分節點已經完成commit。此時應該提交該事務處於prepare狀態的節點。n_prepared < Total Nodes。對比情況三與情況一,僅僅通過prepare節點的數量無法區分,因此應用程序需要在prepare完成之后記錄日志(此時,應用程序起着事務協調者(Transcaction Coordinator)的角色,而根據MariaDB WorkLog#132[5]的說法,TC角色是可以進行”middle engine”優化的,不需要prepare過程,所有MySQL節點xa prepare返回之后,應用程序直接寫commit標識即可,然后再對每個MySQL節點進行xa commit操作。),從而用於區分情況一與情況三。

  情況四:分布式事務對應的MySQL實例,全部完成commit。此時事務已經提交成功,xid不會出現在執行xa recover的任一個節點。不需要特殊處理。

  情況五:未記錄任何prepare日志。那么所有的事務,在各個存儲引擎的crash recover時,都會被回滾,不需要外部特殊處理。

  1.2 MySQL外部XA不足

  通過前面的分析,可知應用程序配合MySQL的XA事務功能,能夠較好的支持分布式環境下的事務。但是,這個支持並不完美,根據我的分析,有可能會出現以下幾個問題:

  • 問題一:主備數據庫的數據不一致。

  MySQL數據庫的主備數據庫的同步,通過Binlog的復制完成。而Binlog是MySQL數據庫內部XA事務的協調者,並且MySQL數據庫為binlog做了優化——binlog不寫prepare日志,只寫commit日志。

  考慮前面提到的情況二,所有的參與節點prepare完成,在進行xa commit前crash。crash recover如果選擇commit此事務。由於binlog在prepare階段未寫,因此主庫中看來,此分布式事務最終提交了,但是此事務的操作並未寫到binlog中,因此也就未能成功復制到備庫,從而導致主備庫數據不一致的情況出現。

  在MySQL 5.5.16版本中做過測試,這個問題實際存在。crash recover之后,對xa recover返回的事務運行xa commit,對應事務提交,但是操作並未寫入binlog,因此無法復制到備庫。

  那么是否回滾所有prepare的事務,就可以避免此問題呢?結論是仍舊不行,不僅不能解決問題一,甚至可能引起問題二。

  • 問題二:同一事務,在各參與節點,最終狀態不一致(部分提交,部分回滾)。

  若回滾所有prepare狀態的分布式事務,會產生問題二。考慮情況三(所有節點完成prepare,部分節點完成commit),該分布式事務對應的節點,部分已經提交,無法回滾,而部分節點回滾。最終導致同一分布式事務,在各參與節點,最終狀態不一致。

  • 問題三:源碼級別問題。MySQL 5.1.49源碼對於外部XA事務處理存在bug,在MySQL 5.5.16版本中,此bug已經被fix。經過驗證發現,在我已下載的MySQL 5.1.61與之后的所有版本,此bug均已經被fix。

  在MySQL 5.1.49中,所有xa recover返回的外部xid,都不能被提交。原因如下:

  當運行xa commit ‘xid_name’命令時,MySQL會判斷當前xid_name的錯誤信息,若存在錯誤信息,那么就在內部將xa commit命令強制轉換為xa rollback。xid_name的狀態存於xid_cache中,在crash recover階段,由函數Handler.cc::xarecover_handlerton調用xid_cache_insert(&xid_cache, x)函數完成插入。MySQL 5.1.49在實現xid_cache_insert函數有bug。

 

  … 
  xs->xa_state=xa_state; 
  xs->xid.set(xid); 
  xs->in_thd=0; 
  xs->rm_error=0; 
  res=my_hash_insert(&xid_cache, (uchar*)xs); 
  …

 

  MySQL 5.1.49中,缺少了xs->rm_error =0這一行,未初始化rm_error,導致xa commit時判斷出錯,無法commit。MySQL 5.5.16已經fix此bug,加上了黑色這一行的初始化,應用程序可以xa commit。

  1.3 不足的解決方案

  從MySQL數據庫外部分布式事務XA不足的分析可以看出,除了實現bug之外,產生其余兩個問題的最大原因,還是在於MySQL針對binlog做的 ”middle engine” 優化,binlog的prepare不寫日志。在MySQL內部XA事務中,這個優化是可行的,因為Binlog本身的角色就是事務協調者(Transaction Coordinator),事務協調者可以不進行prepare [5]。

  但是對於MySQL外部XA事務,Binlog已經不是事務協調者的角色,其也是一個參與者,或者說是Resource Manager。因此Binlog的prepare日志是不可省略的。

  為了解決MySQL數據庫外部XA分布式事務crash recover過程中出現的問題,我覺得只能修改binlog模塊。使binlog模塊在正常運行過程中也區分內部XA事務與外部XA事務。內部XA事務可以仍舊沿用現在的方案;而外部XA分布式事務,需要增加寫prepare日志的功能,已經crash recover時處理prepare日志的功能。

    參考資料

    [1] Sergei Golubchik. Distributed Transaction Processing with MySQL XA

    [2] http://dev.mysql.com/doc/refman/5.1/en/xa.html

    [3] X/Open. Distributed TP: The XA Specification

    [4] 陳思儒. Amoeba

    [5] MariaDB WorkLog#132: Transaction coordinator plugin


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM