MySQL併發複製系列一:binlog組提交

併發複製(Parallel Replication 系列  : Binary Log Group Commit

作者:沃趣科技MySQL數據庫工程師  麻鵬飛


MySQL  Binary log在MySQL 5.1版本後推出主要用於主備複製的搭建,我們回顧下MySQL 在開啓/關閉 Binary Log功能時是如何工作的 。

MySQL沒有開啓Binary log的情況下: 

  •  InnoDB存儲引擎通過redo和undo日誌可以safe crash recovery數據庫,當數據crash recovery時,通過redo日誌將所有已經在存儲引擎內部提交的事務應用redo log恢復,所有已經prepared但是沒有committransactions將會應用undo logroll back。然後客戶端連接時就能看到已經提交的數據存在數據庫內,未提交被回滾地數據需要重新執行。

MySQL開啓Binary log 的情況下:

  • 爲了保證存儲引擎和MySQL數據庫上層的二進制日誌保持一致(因爲備庫通過二進制日誌重放主庫提交的事務,假設主庫存儲引擎已經提交而二進制日誌沒有保持一致,則會使備庫數據丟失造成主備數據不一致),引入二階段提交(two phase commit or 2pc) 

圖1 二階段提交

  MySQL二階段提交流程:

            Storage Engine(InnoDB) transaction prepare階段:即sql語句已經成功執行並生成redo和undo的內存日誌

            Binary log日誌提提交


    • write()將binary log內存日誌數據寫入文件系統緩存

    • fsync()將binary log 文件系統緩存日誌數據永久寫入磁盤


            Storage Engine(InnoDB)內部提交


    • commit階段在存儲引擎內提交( innodb_flush_log_at_trx_commit控制)使undo和redo永久寫入磁盤


    開啓Binary log的MySQL在crash recovery時:

  • 當事務在prepare階段crash,數據庫recovery的時候該事務未寫入Binary log並且存儲引擎未提交,將該事務roll back

  • 當事務在Binary log日誌已經fsync()永久寫入二進制日誌時crash,但是存儲引擎未來得及commit,此時MySQL數據庫recovery的時候將會從二進制日誌的Xid(MySQL數據庫內部分佈式事務XA)中獲取提交的信息重新將該事務重做並commit使存儲引擎和二進制日誌始終保持一致。

 

以上提到單個事務的二階段提交過程,能夠保證存儲引擎和binary log日誌保持一致,但是在併發的情況下怎麼保證存儲引擎和Binary Log提交的順序一致當多個事務併發提交的情況,如果Binary Log和存儲引擎順序不一致會造成什麼影響?

 圖2 InnoDB存儲引擎提交的順序與MySQL上層的二進制日誌順序不同

如上圖:事務按照T1T2T3順序開始執行,將二進制日誌(按照T1、T2、T3順序)寫入日誌文件系統緩存,調用fsync()進行一次group commit將日誌文件永久寫入磁盤,但是存儲引擎提交的順序爲T2T3T1當T2、T3提交事務之後做了一個On-line的backup程序新建一個slave來做replication,那麼事務T1在slave機器restore MySQL數據庫的時候發現未在存儲引擎內提交,T1事務被roll back,此時主備數據不一致(搭建Slave時,change master to的日誌偏移量記錄T3在事務位置之後)。

結論:MySQL數據庫上層二進制日誌的寫入順序和存儲引擎InnoDB層的事務提交順序一致,用於備份及恢復需要,如xtrabackup和innobackpex工具。
      爲了解決以上問題,在早期的MySQL版本,通過prepare_commit_mutex 鎖保證MySQ數據庫上層二進制日誌和Innodb存儲引擎層的事務提交順序一致。


 圖3 通過prepare_commit_mutex保證存儲引擎和二進制日誌順序提交順序一致



圖3可以看出在prepare_commit_mutex,只有當上一個事務commit後釋放鎖,下一個事務纔可以進行prepara操作,並且在每個transaction過程中Binary log沒有fsync()的調用。由於內存數據寫入磁盤的開銷很大,如果頻繁fsync()把日誌數據永久寫入磁盤數據庫的性能將會急劇下降。此時MySQL 數據庫提供sync_binlog參數來設置多少個binlog日誌產生的時候調用一次fsync()把二進制日誌刷入磁盤來提高整體性能,該參數的設置作用:

  • sync_binlog=0,二進制日誌fsync()的操作基於操作系統。

  • sync_binlog=1,每一個transaction commit都會調用一次fsync(),此時能保證數據最安全但是性能影響較大。

  • sync_binlog=N,當數據庫crash的時候至少會丟失N-1個transactions。

圖3 所示MySQL開啓Binary log時使用prepare_commit_mutex和sync_log保證二進制日誌和存儲引擎順序保持一致(通過sync_binlog來控制日誌的刷新頻率),prepare_commit_mutex的鎖機製造成高併發提交事務的時候性能非常差而且二進制日誌也無法group commit。

那麼如何保證MySQL開啓Binary Log日誌後使進制日誌寫入順序和存儲引擎提交順序保持一致並且能夠進行二進制日誌的Group Commit

MySQL 5.6 引入BLGC(Binary Log Group Commit),二進制日誌的提交過程分成三個階段,Flush stage、Sync stage、Commit stage。

那麼事務提交過程簡化爲:

存儲引擎(InnoDB) Prepare    ---->    數據庫上層(Binary Log)   Flush Stage    ---->    Sync Stage    ---->    調存儲引擎(InnoDBCommit stage.

每個stage階段都有各自的隊列,使每個session的事務進行排隊。當一個線程註冊了一個空隊列,該線程就視爲該隊列的leader,後註冊到該隊列的線程爲follower,leader控制隊列中follower的行爲。leader同時帶領當前隊列的所有follower到下一個stage去執行,當遇到下一個stage並非空隊列,此時leader可以變成follower到此隊列中(注:follower的線程不可能變成leader)



 圖4: 二進制日誌三階段提交過程

在 Flush stage:所有已經註冊線程都將寫入binary log緩存

在Sync stage :binary log緩存的數據將會sync到磁盤,當sync_binlog=1時所有該隊列事務的二進制日誌緩存永久寫入磁盤

在 Commit stage:leader根據順序調用存儲引擎提交事務。

當一組事務在進行Commit階段時,其他新的事務可以進行Flush階段,從而使group commit不斷生效。那麼爲了提高group commit中一組隊列的事務數量,MySQL用binlog_max_flush_queue_time來控制在Flush stage中的等待時間,讓Flush隊列在此階段多等待一些時間來增加這一組事務隊列的數量使該隊列到Sync階段可以一次fysn()更多的事務。

 

MySQL 5.7 Parallel replication實現主備多線程複製基於主庫Binary Log Group Commit, 並在Binary log日誌中標識同一組事務的last_commited=N和該組事務內所有的事務提交順序。爲了增加一組事務內的事務數量提高備庫組提交時的併發量引入了binlog_group_commit_sync_delay=N 和binlog_group_commit_sync_no_delay_count=N (注:binlog_max_flush_queue_time MySQL5.7.9及之後版本不再生效)參數,MySQL等待binlog_group_commit_sync_delay毫秒直到達到binlog_group_commit_sync_no_delay_count事務個數時,將進行一次組提交。

 



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章