MySQL binlog 組提交與 XA(分佈式事務、兩階段提交)【轉】

Reference: https://www.cnblogs.com/zhoujinyi/p/5257558.htmlhtml

 

概念:mysql

      XA(分佈式事務)規範主要定義了(全局)事務管理器(TM: Transaction Manager)和(局部)資源管理器(RM: Resource Manager)之間的接口。XA爲了實現分佈式事務,將事務的提交分紅了兩個階段:也就是2PC (tow phase commit),XA協議就是經過將事務的提交分爲兩個階段來實現分佈式事務。sql

兩階段:數據庫

1)prepare 階段服務器

      事務管理器向全部涉及到的數據庫服務器發出prepare"準備提交"請求,數據庫收到請求後執行數據修改和日誌記錄等處理,處理完成後只是把事務的狀態改爲"能夠提交",而後把結果返回給事務管理器。即:爲prepare階段,TM向RM發出prepare指令,RM進行操做,而後返回成功與否的信息給TM。多線程

2)commit 階段併發

      事務管理器收到迴應後進入第二階段,若是在第一階段內有任何一個數據庫的操做發生了錯誤,或者事務管理器收不到某個數據庫的迴應,則認爲事務失敗,回撤全部數據庫的事務。數據庫服務器收不到第二階段的確認提交請求,也會把"能夠提交"的事務回撤。若是第一階段中全部數據庫都提交成功,那麼事務管理器向數據庫服務器發出"確認提交"請求,數據庫服務器把事務的"能夠提交"狀態改成"提交完成"狀態,而後返回應答。即:爲事務提交或者回滾階段,若是TM收到全部RM的成功消息,則TM向RM發出提交指令;否則則發出回滾指令。分佈式

實現:性能

      MySQL中的XA實現分爲:外部XA和內部XA。前者是指咱們一般意義上的分佈式事務實現;後者是指單臺MySQL服務器中,Server層做爲TM(事務協調者),而服務器中的多個數據庫實例做爲RM,而進行的一種分佈式事務,也就是MySQL跨庫事務;也就是一個事務涉及到同一條MySQL服務器中的兩個innodb數據庫(由於其它引擎不支持XA)。優化

1)內部XA的額外功能:XA 將事務的提交分爲兩個階段,而這種實現,解決了 binlog 和 redo log的一致性問題。

      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階段崩潰,也會回滾
複製代碼

簡而言之就是:先寫redo log,再寫binlog,並以binlog寫成功爲事務提交成功的標誌。崩潰恢復是以binlog中的xid和redo log中的xid進行比較,xid在binlog裏存在則提交,不存在則回滾。

    MySQL XA分爲兩類,內部XA與外部XA;

    內部XA用於同一實例下跨多個引擎的事務,由Binlog做爲協調者;

    外部XA用於跨多個MySQL實例的分佈式事務,須要應用層介入做爲協調者(崩潰時的懸掛事務,全局提交仍是回滾,須要由應用層決定,對應用層的實現要求較高);

最多見的內部XA事務存在於binlogInnoDB存儲引擎之間,從而保證了主從環境的數據一致性。

2)binlog 組提交:

      上面介紹事務的兩階段提交過程是5.6以前版本中的實現,有嚴重的缺陷。當sync_binlog=1時,很明顯上述的第二階段中的 write/sync binlog會成爲瓶頸,並且仍是持有全局大鎖(prepare_commit_mutex: prepare 和 commit共用一把鎖),這會致使性能急劇降低。解決辦法就是在MySQL5.6中引進的binlog組提交。

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的大小順序,進行事務組提交了。)
題外話:淘寶對binlog group commit進行了進一步的優化,從XA恢復的邏輯咱們能夠知道,只要保證InnoDB Prepare階段的redo日誌在寫Binlog前完成write/sync便可。所以咱們對Group Commit的第一個stage的邏輯作了些許修改,大概描述以下:
1. InnoDB Prepare,記錄當前的LSN到thd中; 
2. 進入Group Commit的flush stage;Leader蒐集隊列,同時算出隊列中最大的LSN。 
3. 將InnoDB的redo log write/fsync到指定的LSN  (注:這一步就是redo log的組寫入。由於小於等於LSN的redo log被一次性寫入到ib_logfile[0|1]) #放到flush binlog 以後
4. 寫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都進行了優化。

注意:當引入Group Commit後,sync_binlog的含義就變了,假定設爲1000,表示的不是1000個事務後作一次fsync,而是1000個事務組。

3)相關參數:

innodb_support_xa:默認爲true,表示啓用XA,雖然它會致使一次額外的磁盤flush(prepare階段flush redo log). 可是咱們必須啓用,而不能關閉它。由於關閉會致使binlog寫入的順序和實際的事務提交順序不一致,會致使崩潰恢復和slave複製時發生數據錯誤。若是啓用了log-bin參數,而且不止一個線程對數據庫進行修改,那麼就必須啓用innodb_support_xa參數。

相關文章
相關標籤/搜索