您好,登錄后才能下訂單哦!
并發(fā)復制(Parallel Replication) 系列 一 : Binary Log Group Commit
作者:沃趣科技MySQL數(shù)據(jù)庫工程師 麻鵬飛
MySQL Binary log在MySQL 5.1版本后推出主要用于主備復制的搭建,我們回顧下MySQL 在開啟/關閉 Binary Log功能時是如何工作的 。
MySQL沒有開啟Binary log的情況下:
InnoDB存儲引擎通過redo和undo日志可以safe crash recovery數(shù)據(jù)庫,當數(shù)據(jù)crash recovery時,通過redo日志將所有已經在存儲引擎內部提交的事務應用redo log恢復,所有已經prepared但是沒有commit的transactions將會應用undo log做roll back。然后客戶端連接時就能看到已經提交的數(shù)據(jù)存在數(shù)據(jù)庫內,未提交被回滾地數(shù)據(jù)需要重新執(zhí)行。
MySQL開啟Binary log 的情況下:
為了保證存儲引擎和MySQL數(shù)據(jù)庫上層的二進制日志保持一致(因為備庫通過二進制日志重放主庫提交的事務,假設主庫存儲引擎已經提交而二進制日志沒有保持一致,則會使備庫數(shù)據(jù)丟失造成主備數(shù)據(jù)不一致),引入二階段提交(two phase commit or 2pc)
圖1 二階段提交
MySQL二階段提交流程:
Storage Engine(InnoDB) transaction prepare階段:即sql語句已經成功執(zhí)行并生成redo和undo的內存日志
Binary log日志提提交
write()將binary log內存日志數(shù)據(jù)寫入文件系統(tǒng)緩存
fsync()將binary log 文件系統(tǒng)緩存日志數(shù)據(jù)永久寫入磁盤
Storage Engine(InnoDB)內部提交
commit階段在存儲引擎內提交( innodb_flush_log_at_trx_commit控制)使undo和redo永久寫入磁盤
開啟Binary log的MySQL在crash recovery時:
當事務在prepare階段crash,數(shù)據(jù)庫recovery的時候該事務未寫入Binary log并且存儲引擎未提交,將該事務roll back。
當事務在Binary log日志已經fsync()永久寫入二進制日志時crash,但是存儲引擎未來得及commit,此時MySQL數(shù)據(jù)庫recovery的時候將會從二進制日志的Xid(MySQL數(shù)據(jù)庫內部分布式事務XA)中獲取提交的信息重新將該事務重做并commit使存儲引擎和二進制日志始終保持一致。
以上提到單個事務的二階段提交過程,能夠保證存儲引擎和binary log日志保持一致,但是在并發(fā)的情況下怎么保證存儲引擎和Binary Log提交的順序一致?當多個事務并發(fā)提交的情況,如果Binary Log和存儲引擎順序不一致會造成什么影響?
圖2 InnoDB存儲引擎提交的順序與MySQL上層的二進制日志順序不同
如上圖:事務按照T1、T2、T3順序開始執(zhí)行,將二進制日志(按照T1、T2、T3順序)寫入日志文件系統(tǒng)緩存,調用fsync()進行一次group commit將日志文件永久寫入磁盤,但是存儲引擎提交的順序為T2、T3、T1。當T2、T3提交事務之后做了一個On-line的backup程序新建一個slave來做replication,那么事務T1在slave機器restore MySQL數(shù)據(jù)庫的時候發(fā)現(xiàn)未在存儲引擎內提交,T1事務被roll back,此時主備數(shù)據(jù)不一致(搭建Slave時,change master to的日志偏移量記錄T3在事務位置之后)。
結論:MySQL數(shù)據(jù)庫上層二進制日志的寫入順序和存儲引擎InnoDB層的事務提交順序一致,用于備份及恢復需要,如xtrabackup和innobackpex工具。
為了解決以上問題,在早期的MySQL版本,通過prepare_commit_mutex 鎖保證MySQ數(shù)據(jù)庫上層二進制日志和Innodb存儲引擎層的事務提交順序一致。
圖3 通過prepare_commit_mutex保證存儲引擎和二進制日志順序提交順序一致
圖3可以看出在prepare_commit_mutex,只有當上一個事務commit后釋放鎖,下一個事務才可以進行prepara操作,并且在每個transaction過程中Binary log沒有fsync()的調用。由于內存數(shù)據(jù)寫入磁盤的開銷很大,如果頻繁fsync()把日志數(shù)據(jù)永久寫入磁盤數(shù)據(jù)庫的性能將會急劇下降。此時MySQL 數(shù)據(jù)庫提供sync_binlog參數(shù)來設置多少個binlog日志產生的時候調用一次fsync()把二進制日志刷入磁盤來提高整體性能,該參數(shù)的設置作用:
sync_binlog=0,二進制日志fsync()的操作基于操作系統(tǒng)。
sync_binlog=1,每一個transaction commit都會調用一次fsync(),此時能保證數(shù)據(jù)最安全但是性能影響較大。
sync_binlog=N,當數(shù)據(jù)庫crash的時候至少會丟失N-1個transactions。
圖3 所示MySQL開啟Binary log時使用prepare_commit_mutex和sync_log保證二進制日志和存儲引擎順序保持一致(通過sync_binlog來控制日志的刷新頻率),prepare_commit_mutex的鎖機制造成高并發(fā)提交事務的時候性能非常差而且二進制日志也無法group commit。
那么如何保證MySQL開啟Binary Log日志后使二進制日志寫入順序和存儲引擎提交順序保持一致并且能夠進行二進制日志的Group Commit?
MySQL 5.6 引入BLGC(Binary Log Group Commit),二進制日志的提交過程分成三個階段,F(xiàn)lush stage、Sync stage、Commit stage。
那么事務提交過程簡化為:
存儲引擎(InnoDB) Prepare ----> 數(shù)據(jù)庫上層(Binary Log) Flush Stage ----> Sync Stage ----> 調存儲引擎(InnoDB)Commit stage.
每個stage階段都有各自的隊列,使每個session的事務進行排隊。當一個線程注冊了一個空隊列,該線程就視為該隊列的leader,后注冊到該隊列的線程為follower,leader控制隊列中follower的行為。leader同時帶領當前隊列的所有follower到下一個stage去執(zhí)行,當遇到下一個stage并非空隊列,此時leader可以變成follower到此隊列中(注:follower的線程不可能變成leader)
圖4: 二進制日志三階段提交過程
在 Flush stage:所有已經注冊線程都將寫入binary log緩存
在Sync stage :binary log緩存的數(shù)據(jù)將會sync到磁盤,當sync_binlog=1時所有該隊列事務的二進制日志緩存永久寫入磁盤
在 Commit stage:leader根據(jù)順序調用存儲引擎提交事務。
當一組事務在進行Commit階段時,其他新的事務可以進行Flush階段,從而使group commit不斷生效。那么為了提高group commit中一組隊列的事務數(shù)量,MySQL用binlog_max_flush_queue_time來控制在Flush stage中的等待時間,讓Flush隊列在此階段多等待一些時間來增加這一組事務隊列的數(shù)量使該隊列到Sync階段可以一次fysn()更多的事務。
MySQL 5.7 Parallel replication實現(xiàn)主備多線程復制基于主庫Binary Log Group Commit, 并在Binary log日志中標識同一組事務的last_commited=N和該組事務內所有的事務提交順序。為了增加一組事務內的事務數(shù)量提高備庫組提交時的并發(fā)量引入了binlog_group_commit_sync_delay=N 和binlog_group_commit_sync_no_delay_count=N (注:binlog_max_flush_queue_time 在MySQL的5.7.9及之后版本不再生效)參數(shù),MySQL等待binlog_group_commit_sync_delay毫秒直到達到binlog_group_commit_sync_no_delay_count事務個數(shù)時,將進行一次組提交。
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經查實,將立刻刪除涉嫌侵權內容。