mysql 內部xa(兩階段提交)


原文鏈接:https://www.linuxidc.com/Linux/2015-11/124942.htm

 

mysql為了保證redo Log 和binlog一致性,內部事物提交采用xa兩階段提交。
注:redo log為引擎層日志,binlog為server層日志。
 
MySQL中的XA實現分為: 外部XA和內部XA;前者是指我們通常意義上的分布式事務實現;后者是指單台MySQL服務器中,Server層作為TM(事務協調者),而服務器中的多個數據庫實例作為RM,而進行的一種分布式事務,也就是 MySQL跨庫事務;也就是一個事務涉及到同一條MySQL服務器中的兩個innodb數據庫(因為其它引擎不支持XA)。
 

XA 將事務的提交分為兩個階段,而這種實現,解決了 binlog 和 redo log的一致性問題,這就是MySQL內部XA的第三種功能。

MySQL為了兼容其它非事物引擎的復制,在server層面引入了 binlog, 它可以記錄所有引擎中的修改操作,因而可以對所有的引擎使用復制功能;MySQL在4.x 的時候放棄redo的復制策略而引入binlog的復制(淘寶丁奇)。

但是引入了binlog,會導致一個問題——binlog和redo log的一致性問題:一個事務的提交必須寫redo log和binlog,那么二者如何協調一致呢?事務的提交以哪一個log為標准?如何判斷事務提交?事務崩潰恢復如何進行?
MySQL通過 兩階段提交( 內部XA的兩階段提交)很好地解決了這一問題:
第一階段:InnoDB prepare,持有prepare_commit_mutex,並且write/sync redo log; 將回滾段設置為Prepared狀態,binlog不作任何操作;
第二階段:包含兩步,1> write/sync Binlog; 2> InnoDB commit (寫入COMMIT標記后釋放prepare_commit_mutex);
以 binlog 的寫入與否作為事務提交成功與否的標志,innodb commit標志並不是事務成功與否的標志。因為此時的事務崩潰恢復過程如下:
1> 崩潰恢復時,掃描最后一個Binlog文件,提取其中的xid;  
2> InnoDB維持了狀態為Prepare的事務鏈表,將這些事務的xid和Binlog中記錄的xid做比較,如果在Binlog中存在,則提交,否則回滾事務。
通過這種方式,可以讓InnoDB和Binlog中的事務狀態保持一致。如果在寫入innodb commit標志時崩潰,則恢復時,會重新對commit標志進行寫入;
在prepare階段崩潰,則會回滾,在write/sync binlog階段崩潰,也會回滾。這種事務提交的實現是MySQL5.6之前的實現。
 
 binlog 組提交
上面的事務的兩階段提交過程是5.6之前版本中的實現,有嚴重的缺陷。當sync_binlog=1時,很明顯上述的第二階段中的 write/sync binlog會成為瓶頸,而且還是持有全局大鎖(prepare_commit_mutex: prepare 和 commit共用一把鎖),這會導致性能急劇下降。解決辦法就是MySQL5.6中的 binlog組提交。  
 MySQL5.6中的binlog group commit:

Binlog Group Commit的過程拆分成了三個階段:

1> flush stage 將各個線程的binlog從cache寫到文件中; 

2> sync stage 對binlog做fsync操作(如果需要的話;最重要的就是這一步,對多個線程的binlog合並寫入磁盤);

3> commit stage 為各個線程引擎層的事務commit(這里不用寫redo log,在prepare階段已寫)。每個stage同時只有一個線程在操作。(分成三個階段,每個階段的任務分配給一個專門的線程,這是典型的並發優化)

這種實現的 優勢在於三個階段可以並發執行,從而提升效率。注意prepare階段沒有變,還是write/sync redo log.
(另外:5.7中引入了 MTS:多線程slave復制,也是通過binlog組提交實現的,在binlog組提交時,給每一個組提交打上一個seqno,然后在slave中就可以按照master中一樣按照seqno的大小順序,進行事務組提交了。)
 
  MySQL5.7中的binlog group commit:

淘寶對binlog group commit進行了進一步的優化,其原理如下:

從XA恢復的邏輯我們可以知道,只要保證InnoDB Prepare的redo日志在寫Binlog前完成write/sync即可。因此我們對Group Commit的第一個stage的邏輯做了些許修改,大概描述如下:

 Step1. InnoDB Prepare,記錄當前的LSN到thd中; 
 Step2. 進入Group Commit的flush stage;Leader搜集隊列,同時算出隊列中最大的LSN。 
 Step3. 將InnoDB的redo log write/fsync到指定的LSN  (注:這一步就是redo log的組寫入。因為小於等於LSN的redo log被一次性寫入到ib_logfile[0|1])
 Step4. 寫Binlog並進行隨后的工作(sync Binlog, InnoDB commit , etc)

也就是將 redo log的write/sync延遲到了 binlog group commit的 flush stage 之后,sync binlog之前。

通過延遲寫redo log的方式,顯式的為redo log做了一次組寫入(redo log group write),並減少了(redo log) log_sys->mutex的競爭。

也就是將 binlog group commit 對應的redo log也進行了 group write. 這樣binlog 和 redo log都進行了優化。

參數innodb_support_xa默認為true,表示啟用XA,雖然它會導致一次額外的磁盤flush(prepare階段flush redo log). 但是我們必須啟用,而不能關閉它。因為關閉會導致binlog寫入的順序和實際的事務提交順序不一致,會導致崩潰恢復和slave復制時發生數據錯誤。如果啟用了log-bin參數,並且不止一個線程對數據庫進行修改,那么就必須啟用innodb_support_xa參數。

 


免責聲明!

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



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