前些天在查看關於innodb_flush_log_at_trx_commit的官網解釋時產生了一些疑問,關於
innodb_flush_log_at_trx_commit參數的詳細解釋參見官網:
https://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_flush_log_at_trx_commit
InnoDB
log buffer are written to the log file after each transaction commit and the log file is flushed to disk approximately once per second.

- sync_binlog=0:表示fsync()的調用完全交給操作系統,即文件系統緩存中的binlog是否刷新到disk完全由操作系統控制。
- sync_binlog=1:表示在發出事務提交請求時,binlog一定會被固化到disk,write()跳過文件系統緩存直接寫入disk。
- sync_binlog=N(N>1):數據庫崩潰時,可能會丟失N-1個事務。
- 此值為0表示:redo log buffer的內容每秒會被寫入文件系統緩存的redo log里,同時被flush(固化)到disk上的redo log file中。
- 此值為1表示:redo log buffer的內容會在事務commit時被寫入文件系統緩存的redo log里,同時被flush(固化)到disk上的redo log file中。
- 此值為2表示:redo log buffer的內容會在事務commit時被寫入文件系統緩存的redo log里,而文件系統緩存的redo log每秒一次被flush(固化)到disk上的redo log file中。
注意redo和undo是在事務執行過程中就即時生成的,且早於數據庫真正被修改,這被稱作write ahead logging(WAL),undo的disk文件位置默認在系統表空間中,5.6以后也可以指定獨立的undo表空間。
使用 prepare_commit_mutex 來保證事務提交的順序,只有當上一個事務 commit 后釋放鎖,下個事務才可以進行 prepare 操作,這樣並發事務之間的mutex爭用可能比較高。
由於內存數據寫入磁盤的開銷很大,如果頻繁 fsync() 把日志數據永久寫入磁盤,數據庫的性能將會急劇下降。高並發事務帶來的頻繁磁盤寫會導致事務提交等待帶來性能瓶頸,為此提供 sync_binlog 參數來設置多少個 binlog 日志產生的時候調用一次 fsync() 把二進制日志刷入磁盤來提高整體性能,但這可能導致主從不一致。
因此針對innodb事務出現了binlog的組提交方式,其基本原理就是將多個並發事務的binlog(3個以上)通過隊列機制一次性寫入磁盤,從而減小磁盤寫次數,也避免了prepare_commit_mutex 的爭用。
雖然組提交機制可以有效的提升高並發時的日志寫效率,但是官網也明確說明只有高並發時效果才比較顯著,如果數據庫沒什么並發反而效率還會降低。
改進方案:
Mysql5.6 引入了組提交,並將提交過程分成 Flush stage、Sync stage、Commit stage 三個階段。其實簡單的說就是加入隊列機制使得binlog寫入順序與事務執行順序一致,加入隊列的最大好處就是可以不獲取prepare_commit_mutex鎖也能實現不降低性能的日志順序寫。
Binlog組提交的基本思想是,引入隊列機制保證Innodb commit順序與binlog落盤順序一致,並將事務分組,組內的binlog刷盤動作交給一個事務進行,實現組提交目的。在MySQL數據庫上層進行提交時首先按順序將其放入一個隊列中,隊列中的第一個事務稱為leader,其他事務稱為follow,leader控制着follow的行為。
- Flush Stage
1) 持有Lock_log mutex [leader持有,follower等待]。
2) 獲取隊列中的一組binlog(隊列中的所有事務)。
3) 將binlog buffer到I/O cache。
4) 通知dump線程dump binlog。
- Sync Stage
1) 釋放Lock_log mutex,持有Lock_sync mutex[leader持有,follower等待]。
2) 將一組binlog 落盤(sync動作,最耗時,假設sync_binlog為1)。
- Commit Stage
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。當有一組事務在進行commit階段時,其他新事物可以進行Flush階段,從而使group commit不斷生效。當然group commit的效果由隊列中事務的數量決定,若每次隊列中僅有一個事務,那么可能效果和之前差不多,甚至會更差。但當提交的事務越多時,group commit的效果越明顯,數據庫性能的提升也就越大。
與 binlog 組提交相關的參數主要包括了如下兩個:
- binlog_max_flush_queue_time
單位為微秒,用於從 flush 隊列中取事務的超時時間,這主要是防止並發事務過高,導致某些事務的 RT 上升,詳細內容可以查看函數MYSQL_BIN_LOG::process_flush_stage_queue() 。
注意:該參數在 5.7 之后已經取消了。
- binlog_order_commits
當設置為 0 時,事務可能以和 binlog 不同的順序提交,其性能會有稍微提升,但並不是特別明顯.