mysql 5.6 binlog组提交
http://blog.itpub.net/15480802/viewspace-1411356
?
Redo組提交Redo提交流程大致如下
lock log->mutex
write redo log buffer to disk
unlock log->mutex
fsync
?
Fsync寫磁盤耗時(shí)較長(zhǎng)且不占用log->mutex,也就是其執(zhí)行期間其他線程可以write log buffer;
假定一次fsync需要10ms,而寫buffer只需要1ms,則fsync執(zhí)行期間最多可以有10條redo record寫入buffer,則下次調(diào)用fsync時(shí)可一次性寫10條記錄;
?
?
binlog組提交
Mysql?從?5.0?開(kāi)始支持2PC,在代碼實(shí)現(xiàn)時(shí)為了保證Binlog中的事務(wù)順序和事務(wù)commit順序一致,放棄了Group Commit。
如果Binlog順序不一致,那么備庫(kù)就無(wú)法確保和主庫(kù)有一致的數(shù)據(jù)。這個(gè)問(wèn)題直到?mysql 5.5?才開(kāi)始部分修復(fù),到?mysql 5.6?完全修復(fù)。
在?mysql 5.5?中,只有當(dāng)?sync_binlog = 0?時(shí),才能使用?group commit,在?mysql 5.6中都可以進(jìn)行?group commit。
?
2PC下的事務(wù)提交流程
1. Prepare Innodb:
?? a) Write prepare record to Innodb's log buffer
?? b) Sync log file to disk? -- redo組提交
?? c) Take prepare_commit_mutex
2. "Prepare" binary log:
?? a) Write transaction to binary log
?? b) Sync binary log based on sync_binlog
3. Commit Innodb:
?? a) Write commit record to log
?? b) Release prepare_commit_mutex
?? c) Sync log file to disk
?? d) Innodb locks are released
4. "Commit" binary log:
?? a) Nothing necessary to do here.
?
?
?
不足
為保證binlog按順序?qū)?#xff0c;prepare redo階段獲取prepare_commit_mutex,直到sync redo前才釋放;一次只能有一個(gè)事務(wù)可獲取該mutex,阻礙了group commit;
另外,一次完整的事務(wù)提交需要調(diào)用3次fsync,效率很低;
?
改進(jìn)
1?減少fsync
crash recovery時(shí)先查看redo log,找出prepared但沒(méi)有commited或aborted的事務(wù)列表,然后檢查binlog,如果binlog記錄了該事務(wù),則將其commit否則rollback;
由此可見(jiàn),事務(wù)恢復(fù)時(shí)取決于binlog是否有記錄,因此commit innodb時(shí)無(wú)須調(diào)用立即fsync(此時(shí)binlog已寫入,就算crash也能保證事務(wù)提交);
?
2?細(xì)化binlog commit代碼,實(shí)現(xiàn)組提交
在Oracle MySQL 5.6.15中,binlog group commit模塊被重寫,這個(gè)過(guò)程分為3個(gè)stage:flush/sync/commit;
?
?
5.6的2PC提交流程如下
1. Ask binary log (i.e. coordinator to prepare
? a) Request to release locks earlier
? b) Prepare Innodb (Callback to (2.a))
2. Prepare Innodb:
? a) Write prepare record to Innodb log buffer
? b) Sync log file to disk
3. Ask binary log (i.e. coordinator) to commit
? a) Lock access to flush stage
? b) Write a set of transactions to the binary log
? c) Unlock access to flush stage
? d) Lock access to sync stage
? e) Flush the binary log to disk
? f) Unlock access to sync stage
? g) Lock access to commit stage
? h) Commit Innodb (Callback to (4.a))
? i) Unlock access to commit stage
4. Commit Innodb
? a) Write a commit record to Innodb log buffer
?
binlog提交被細(xì)化為3個(gè)處理階段,每一階段都有l(wèi)ock保護(hù)(此時(shí)redo已經(jīng)調(diào)用fsync,事務(wù)尚未提交);
?
這3個(gè)階段負(fù)責(zé)批量讀取binlog并調(diào)用fsync,而后以同樣順序提交事務(wù)(可選);
第一個(gè)進(jìn)入處理階段的事務(wù)擔(dān)當(dāng)Leader的角色,剩余的為follower,后者釋放所有的latch并等待,直至leader完成commit;
leader獲取所有排隊(duì)等待的事務(wù)并處理,進(jìn)入下一個(gè)處理階段時(shí),如果隊(duì)列為空則仍是leader,否則降級(jí)為follower;
1. Flush Stage
leader會(huì)不斷讀取flush queue直到隊(duì)列為空或者超時(shí),這樣允許處理過(guò)程中新加入的事務(wù)也能得到及時(shí)處理;
leader將排隊(duì)的事務(wù)寫入binlog buffer,當(dāng)隊(duì)列為空時(shí)則進(jìn)入下一階段;
超時(shí)機(jī)制避免了事務(wù)長(zhǎng)時(shí)間等待,
2. Sync Stage
調(diào)用fsyc,一次刷新多個(gè)事務(wù);
3. Commit Stage
提交事務(wù),保證所有事務(wù)提交順序同寫入binlog一致(innodb hot backup);?為了提升性能,也可選擇不按次序提交;
?
代碼實(shí)現(xiàn)
Binlog原本實(shí)現(xiàn)了handlerton接口,包括commit()/rollback()等方法,5.6引入新機(jī)制
public class MYSQL_BIN_LOG: public TC_LOG
{
? int open_connection(THD* thd);
? int close_connection(THD* thd);
? int commit(THD *thd, bool all);
? int rollback(THD *thd, bool all);
? int savepoint_set(THD* thd, SAVEPOINT *sv);
? int savepoint_release(THD* thd, SAVEPOINT *sv);
? int savepoint_rollback(THD* thd, SAVEPOINT *sv);
};
?
int MYSQL_BIN_LOG::commit(THD *thd, bool all)
{
? /* Call batch_commit(). */
}
?
int MYSQL_BIN_LOG::batch_commit(THD* thd, bool all)
{
--將事務(wù)加入flush queue,第一個(gè)事務(wù)為leader,follower阻塞直至完成commit;
? if (change_stage(thd, Stage_manager::FLUSH_STAGE, thd, NULL, &LOCK_log))
??? return finish_commit(thd->commit_error);
?
--將事務(wù)寫入binlog
? THD *flush_queue= NULL; /* Gets a pointer to the flush_queue */
? error= flush_stage_queue(&wait_queue);
?
? if (change_stage(thd, Stage_manager::SYNC_STAGE, wait_queue, &LOCK_log, &LOCK_sync))
??? return finish_commit(thd->commit_error);
?
--依據(jù)sync_binlog選項(xiàng)調(diào)用fsync,5.5卻只能將sync_binlog=0
? THD *sync_queue= NULL; /* Gets a pointer to the sync_queue */
? error= sync_stage_queue(&sync_queue);
?
--根據(jù)opt_binlog_order_commits,可以按binlog寫入順序提交事務(wù),也可以讓線程調(diào)用handlerton->commit各自提交;
? if (opt_binlog_order_commits)
? {
??? if (change_stage(thd, Stage_manager::COMMIT_STAGE,
???????????????????? final_queue, &LOCK_sync, &LOCK_commit))
????? return finish_commit(thd);
??? THD *commit_queue= NULL;
?
??? error= commit_stage_queue(&commit_queue);
??? mysql_mutex_unlock(&LOCK_commit);
??? final_queue= commit_queue;
? }
? else
? {
??? final_queue= sync_queue;
??? mysql_mutex_unlock(&LOCK_sync);
? }
?
--通知follower,要么提交事務(wù)(opt_binlog_order_commits=false)要么通知客戶端;
? stage_manager.signal_done(final_queue);
?
? return finish_commit(thd);
}
?
參考資料
http://dev.mysql.com/worklog/task/?id=5223
http://mysqlmusings.blogspot.co.uk/2012/06/binary-log-group-commit-in-mysql-56.html?_sm_au_=iDV88W54k66P05L7
?
?
后記:
看到淘寶分享的一篇帖子,在5.6的基礎(chǔ)上還有優(yōu)化的空間,要保證一個(gè)事務(wù)能成功恢復(fù),只需要保證在binlog commit前將對(duì)應(yīng)事務(wù)的redo entry寫入磁盤即可,則redo commit/sync完全可以從redo prepare后移到binlog prepare,將其放于flush stage和commit stage之間,將原本N次的log_sys->mutex獲取次數(shù)降為1次,fsync也變?yōu)?次;
問(wèn)題
每個(gè)事務(wù)都要保證其Prepare的事務(wù)被write/fsync到redo log文件。盡管某個(gè)事務(wù)可能會(huì)幫助其他事務(wù)完成redo 寫入,但這種行為是隨機(jī)的,并且依然會(huì)產(chǎn)生明顯的log_sys->mutex開(kāi)銷。
優(yōu)化
從XA恢復(fù)的邏輯我們可以知道,只要保證InnoDB Prepare的redo日志在寫B(tài)inlog前完成write/sync即可。因此我們對(duì)Group Commit的第一個(gè)stage的邏輯做了些許修改,大概描述如下:
Step1. InnoDB Prepare,記錄當(dāng)前的LSN到thd中;?
注:原本此階段需要獲取log->mutex進(jìn)行的寫文件取消,延遲到下一階段;在原有fsync組提交的基礎(chǔ)上實(shí)現(xiàn)寫文件組提交。
Step2. 進(jìn)入Group Commit的flush stage;Leader搜集隊(duì)列,同時(shí)算出隊(duì)列中最大的LSN。
Step3. 將InnoDB的redo log write/fsync到指定的LSN?
Step4. 寫B(tài)inlog并進(jìn)行隨后的工作(sync Binlog, InnoDB commit , etc)
通過(guò)延遲寫redo log的方式,顯式的為redo log做了一次組寫入,并減少了log_sys->mutex的競(jìng)爭(zhēng)。
目前官方MySQL已經(jīng)根據(jù)我們r(jià)eport的bug#73202鎖提供的思路,對(duì)5.7.6的代碼進(jìn)行了優(yōu)化,對(duì)應(yīng)的Release Note如下:
When using InnoDB with binary logging enabled, concurrent transactions written in the InnoDB redo log are now grouped together before synchronizing to disk when innodb_flush_log_at_trx_commit is set to 1, which reduces the amount of synchronization operations. This can lead to improved performance.
簡(jiǎn)單測(cè)試了下,使用sysbench, update_non_index.lua, 100張表,每張10w行記錄,innodb_flush_log_at_trx_commit=2, sync_binlog=1000,關(guān)閉Gtid
并發(fā)線程 ? ? ? ?原生 ? ? ? ? ? ? ? ? ?修改后
32 ? ? ? ? ? ? 25600 ? ? ? ? ? ? ? ?27000
64 ? ? ? ? ? ? 30000 ? ? ? ? ? ? ? ?35000
128 ? ? ? ? ? ?33000 ? ? ? ? ? ? ? ?39000
256 ? ? ? ? ? ?29800 ? ? ? ? ? ? ? ?38000
總結(jié)
以上是生活随笔為你收集整理的mysql 5.6 binlog组提交的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 『协议』XML-RPC 协议规格说明
- 下一篇: esxi 部署模板_vSphere使用模