您好,登錄后才能下訂單哦!
今天就跟大家聊聊有關如何理解MYSQL GroupCommit,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內容,希望大家根據這篇文章可以有所收獲。
組提交(group commit)是MYSQL處理日志的一種優化方式,主要為了解決寫日志時頻繁刷磁盤的問題。組提交伴隨著MYSQL的發展不斷優化,從最初只支持redo log 組提交,到目前5.6官方版本同時支持redo log 和binlog組提交。組提交的實現大大提高了mysql的事務處理性能,下文將以innodb 存儲引擎為例,詳細介紹組提交在各個階段的實現原理。
redo log的組提交
WAL(Write-Ahead-Logging)是實現事務持久性的一個常用技術,基本原理是在提交事務時,為了避免磁盤頁面的隨機寫,只需要保證事務的redo log寫入磁盤即可,這樣可以通過redo log的順序寫代替頁面的隨機寫,并且可以保證事務的持久性,提高了數據庫系統的性能。雖然WAL使用順序寫替代了隨機寫,但是,每次事務提交,仍然需要有一次日志刷盤動作,受限于磁盤IO,這個操作仍然是事務并發的瓶頸。
組提交思想是,將多個事務redo log的刷盤動作合并,減少磁盤順序寫。Innodb的日志系統里面,每條redo log都有一個LSN(Log Sequence Number),LSN是單調遞增的。每個事務執行更新操作都會包含一條或多條redo log,各個事務將日志拷貝到log_sys_buffer時(log_sys_buffer 通過log_mutex
保護),都會獲取當前最大的LSN,因此可以保證不同事務的LSN不會重復。那么假設三個事務Trx1,Trx2和Trx3的日志的最大LSN分別為LSN1,LSN2,LSN3(LSN1<LSN2<LSN3),它們同時進行提交,那么如果Trx3日志先獲取到log_mutex進行落盤,它就可以順便把[LSN1---LSN3]這段日志也刷了,這樣Trx1和Trx2就不用再次請求磁盤IO。組提交的基本流程如下:
獲取 log_mutex
若flushed_to_disk_lsn>=lsn,表示日志已經被刷盤,跳轉5
若 current_flush_lsn>=lsn,表示日志正在刷盤中,跳轉5后進入等待狀態
將小于LSN的日志刷盤(flush and sync)
退出log_mutex
備注:lsn表示事務的lsn,flushed_to_disk_lsn和current_flush_lsn分別表示已刷盤的LSN和正在刷盤的LSN。
redo log 組提交優化
我們知道,在開啟binlog的情況下,prepare階段,會對redo log進行一次刷盤操作(innodb_flush_log_at_trx_commit=1),確保對data頁和undo 頁的更新已經刷新到磁盤;commit階段,會進行刷binlog操作(sync_binlog=1),并且會對事務的undo log從prepare狀態設置為提交狀態(可清理狀態)。通過兩階段提交方式(innodb_support_xa=1),可以保證事務的binlog和redo log順序一致。二階段提交過程中,mysql_binlog作為協調者,各個存儲引擎和mysql_binlog作為參與者。故障恢復時,掃描最后一個binlog文件(在flush階段,判斷binlog是否超過閥值,進行rotate binlog文件,rotate的binlog文件中對應的事務一定是已經提交的,處于prepared的事務的binlog還沒有刷進來,因為還沒進入ordered_commit函數),提取其中的xid;重做檢查點以后的redo日志,讀取事務的undo段信息,搜集處于prepare階段的事務鏈表,將事務的xid與binlog中的xid對比,若存在,則提交,否則就回滾。
通過上述的描述可知,每個事務提交時,都會觸發一次redo flush動作,由于磁盤讀寫比較慢,因此很影響系統的吞吐量。淘寶童鞋做了一個優化,將prepare階段的刷redo動作移到了commit(flush-sync-commit)的flush階段之前,保證刷binlog之前,一定會刷redo。這樣就不會違背原有的故障恢復邏輯。移到commit階段的好處是,可以不用每個事務都刷盤,而是leader線程幫助刷一批redo。如何實現,很簡單,因為log_sys->lsn始終保持了當前最大的lsn,只要我們刷redo刷到當前的log_sys->lsn,就一定能保證,將要刷binlog的事務redo日志一定已經落盤。通過延遲寫redo方式,實現了redo log組提交的目的,而且減少了log_sys->mutex的競爭。目前這種策略已經被官方mysql5.7.6引入。
兩階段提交
在單機情況下,redo log組提交很好地解決了日志落盤問題,那么開啟binlog后,binlog能否和redo log一樣也開啟組提交?首先開啟binlog后,我們要解決的一個問題是,如何保證binlog和redo log的一致性。因為binlog是Master-Slave的橋梁,如果順序不一致,意味著Master-Slave可能不一致。MYSQL通過兩階段提交很好地解決了這一問題。Prepare階段,innodb刷redo log,并將回滾段設置為Prepared狀態,binlog不作任何操作;commit階段,innodb釋放鎖,釋放回滾段,設置提交狀態,binlog刷binlog日志。出現異常,需要故障恢復時,若發現事務處于Prepare階段,并且binlog存在則提交,否則回滾。通過兩階段提交,保證了redo log和binlog在任何情況下的一致性。
binlog的組提交
回到上節的問題,開啟binlog后,如何在保證redo log-binlog一致的基礎上,實現組提交。因為這個問題,5.6以前,mysql在開啟binlog的情況下,無法實現組提交,通過一個臭名昭著的prepare_commit_mutex,將redo log和binlog刷盤串行化,串行化的目的也僅僅是為了保證redo log-Binlog一致,但這種實現方式犧牲了性能。這個情況顯然是不能容忍的,因此各個mysql分支,mariadb,facebook,perconal等相繼出了補丁改進這一問題,mysql官方版本5.6也終于解決了這一問題。由于各個分支版本解決方法類似,我主要通過分析5.6的實現來說明實現方法。
binlog組提交的基本思想是,引入隊列機制保證innodb commit順序與binlog落盤順序一致,并將事務分組,組內的binlog刷盤動作交給一個事務進行,實現組提交目的。binlog提交將提交分為了3個階段,FLUSH階段,SYNC階段和COMMIT階段。每個階段都有一個隊列,每個隊列有一個mutex保護,約定進入隊列第一個線程為leader,其他線程為follower,所有事情交由leader去做,leader做完所有動作后,通知follower刷盤結束。binlog組提交基本流程如下:
FLUSH 階段
1) 持有Lock_log mutex [leader持有,follower等待]
2) 獲取隊列中的一組binlog(隊列中的所有事務)
3) 將binlog buffer到I/O cache
4) 通知dump線程dump binlog
SYNC階段
1) 釋放Lock_log mutex,持有Lock_sync mutex[leader持有,follower等待]
2) 將一組binlog 落盤(sync動作,最耗時,假設sync_binlog為1)
COMMIT階段
1) 釋放Lock_sync mutex,持有Lock_commit mutex[leader持有,follower等待]
2) 遍歷隊列中的事務,逐一進行innodb commit
3) 釋放Lock_commit mutex
4) 喚醒隊列中等待的線程
說明:由于有多個隊列,每個隊列各自有mutex保護,隊列之間是順序的,約定進入隊列的一個線程為leader,因此FLUSH階段的leader可能是SYNC階段的follower,但是follower永遠是follower。
通過上文分析,我們知道MYSQL目前的組提交方式解決了一致性和性能的問題。通過二階段提交解決一致性,通過redo log和binlog的組提交解決磁盤IO的性能。下面我整理了Prepare階段和Commit階段的框架圖供各位參考。
看完上述內容,你們對如何理解MYSQL GroupCommit有進一步的了解嗎?如果還想了解更多知識或者相關內容,請關注億速云行業資訊頻道,感謝大家的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。