MySQL 組提交
prepare_commit_mutex鎖
MySQL5.6以前,爲了保證數據庫上層二進制日誌的寫入順序和InnoDB層的事務提交順序一致,MySQL數據庫內部使用了prepare_commit_mutex鎖。但是持有這把鎖之後,會導致組提交失敗。鎖的持有與釋放在二階段中如下:
InnoDB prepare (持有prepare_commit_mutex);
write/sync Binlog;
InnoDB commit (寫入COMMIT標記後釋放prepare_commit_mutex)。
這樣事務提交就是一個一個執行,導致組提交失敗。
Binary Log Group Commit(BLGC)
MySQL5.6通過BLGC的方式實現了binlog的組提交。
binlog組提交的基本思想是,引入隊列機制保證innodb commit順序與binlog落盤順序一致,並將事務分組,組內的binlog刷盤動作交給一個事務進行,實現組提交目的。
binlog提交將提交分爲了3個階段,FLUSH階段,SYNC階段和COMMIT階段。每個階段都有一個隊列,隊列中的第一個事務稱爲leader,其他事務稱爲follower,leader控制着follower的行爲。BLGC的步驟分爲以下三個階段:
FLUSH階段:
持有Lock_log mutex [leader持有,follower等待]
獲取隊列中的一組binlog(隊列中的所有事務)
將binlog buffer到I/O cache
通知dump線程dump binlog
SYNC階段:
釋放Lock_log mutex,持有Lock_sync mutex[leader持有,follower等待]
將一組binlog 落盤(sync動作,最耗時,也是group commit實現了的優化的重點所在)
COMMIT階段:
釋放Lock_sync mutex,持有Lock_commit mutex[leader持有,follower等待]
遍歷隊列中的事務,逐一進行innodb commit(這裏不用寫redo log,在prepare階段已寫)
釋放Lock_commit mutex
喚醒隊列中等待的線程
每個stage分配一個線程進行操作。
這種實現的優勢在於三個階段可以併發執行,從而提升效率。(PS:innodb prepare階段沒有變,還是write/sync redo log,打上prepare標記)
每個stage都有自己的隊列。每個隊列各自有mutex保護,隊列之間是順序的。只有flush完成後,才能進入到sync階段的隊列中;sync完成後,才能進入到commit階段的隊列中。但是,這三個階段的作業是可以同時併發執行的,即當一組事務在進行commit階段時,其他新事務可以進行flush階段,實現了group commit。
當一個事務來到一個stage是一個空隊列,那麼他就是leader,後面來的事務就是follower,leader控制隊列中follower的行爲。如果leader帶着自己的follower去下一個stage,是非空隊列,那麼leader變成follower。但是follower不會變成leader。
Tips:當引入Group Commit後,sync_binlog的含義就變了,假定設爲1000,表示的不是1000個事務後做一次fsync,而是1000個事務組。也就是說,當設置sync_binlog=1,binlog還未落盤,此時系統crash,會丟失對應的最後一個事務組;如果這個事務組內有10個事務,那麼這10個事務都會丟失。
如何查看是否屬於一個事務組:通過mysqlbinlog可以查看binlog日誌中last_committed值,如果值一樣,表明是在同一事務組內。
### INSERT INTO `wukong_test`.`wukong` ### SET ### @1=3 /* INT meta=0 nullable=1 is_null=0 */ ### @2='ccccc' /* VARSTRING(80) meta=80 nullable=1 is_null=0 */ # at 496468 #170527 4:17:35 server id 12001 end_log_pos 496499 CRC32 0xd6e7f69f Xid = 5556 COMMIT/*!*/; # at 496499 #170527 4:17:35 server id 12001 end_log_pos 496564 CRC32 0x28816d5c GTID last_committed=1845 sequence_number=1846 SET @@SESSION.GTID_NEXT= '0a646c88-36e2-11e7-937d-fa163ed7a7b1:3624'/*!*/; # at 496564 #170527 4:17:35 server id 12001 end_log_pos 496632 CRC32 0x03150d48 Query thread_id=1852 exec_time=0 error_code=0 SET TIMESTAMP=1495873055/*!*/; BEGIN