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