一、purge
delete和update不会删除数据,因为MVCC机制的存在,可能数据还有别的事务在引用。因此delete和update的删除操作最终在purge线程中完成。
为了节省存储空间,innodb的一个页上允许多个事务的undo log存在,后面事务产生的undo log总在最后面。
此外,innodb引擎还有一个history列表,它根据事务提交的顺序,将undo log进行链接。
例如下面的history列表,灰色代表还有事务引用。
在执行purge过程中,innodb引擎会先从history列表中找到要清理的记录,这里为trx1,清理之后,innodb会继续在trx1的undo log所在的页继续寻找是否存在需要被清理的记录,这里找到trx3,接着找到trx5,但发现trx5被其它事务引用不能删除,故再次到history列表中查找,这次查找到trx2,接着在trx2的undo log所在的页找到trx6、trx4的记录删除,于是undo page 可以被重用。
动态参数innodb_purge_batch_size用来设置每次purge需要清理的undo page数量。在1.2版本前默认为20,1.2版本后默认为300。参数设置越大,每次回收的undo page越多,但设置太大,会使CPU和IO资源被占用太多影响数据库性能。
动态参数innodb_max_purge_lag用来控制history列表的长度,若长度大于该参数时,其会延缓DML的操作,默认为0,表示不会对history列表长度做任何控制。大于0时,就会延缓DML的操作,1.2版本引入innodb_max_purge_delay参数来控制delay的最大毫秒数。
二、group commit
如果事务为非只读事务,则事务提交时需要执行一次fsync操作,以保证重做日志刷到磁盘上。因为磁盘的fsync的性能是有限的,因此,数据库提供了group commit功能来提升效率,即一次fsync操作可以刷新确保多个事务的日志被写入文件。InnoDB引擎在事务提交时会进行2个阶段的操作:
1. 修改内存中事务对应的信息,并且将日志写入重做日志缓冲;
2. 调用fsync将确保日志从缓冲写入到磁盘中。
步骤2比1慢很多,因为需要与磁盘进行交互。但当有事务进行这个过程时,其它事务可以进行步骤1的操作,正在提交的事务完成操作后,再进行步骤2时,可以将多个事务的重做日志通过一个fsync操作刷到磁盘,这样大大减小了磁盘的压力,从而提高数据库的性能。对于写入频繁的的操作,group commit效果显著。
在1.2版本之前,开启二进制日志后,group commit功能会失效,因为为了保证InnoDB引擎中的事务和二进制日志之间的一致性,二者之间使用了两阶段事务,步骤如下:
1)当事务提交时InnoDB存储引擎进行prepare操作;
2)MySQL数据库上层写入二进制日志。
3)InnoDB引擎将日志写入重做日志文件。
• 修改内存中事务对应的信息,并将日志写入重做日志缓冲;
• 调用fsync确保日志从重做日志缓冲写入磁盘。
一旦步骤2)完成,就确保了事务的提交,即使步骤3)发生宕机,每个步骤都需要一次fsync操作才能保证上下两层数据的一致性。步骤2)的fsync由参数sync_binlog控制,步骤3)的fsync由参数innodb_flush_log_at_trx_commit控制,整个过程如下图所示:
为了保证MySQL数据库上层的二进制日志的写入顺序和InnoDB的事务提交顺序一致,MySQL数据库内部使用了prepare_commit_mutex这个锁,但启用这个锁后,步骤3)中的第一步不可以在其它事务在执行第二步时进行,从而导致group commit失败。
MySQL5.6解决了这个问题,解决方案称为Binary Log Group Commit(BLGC),实现方式是将事务的提交过程分为几个步骤来进行。
在mysql上层进行提交时,先按顺序放入一个队列中,队列中的第一个事务称为Leader,其它事务称为follower,leader控制着follower的行为,BLGC分为以下三个阶段:
• Flush阶段,将每个事务的二进制日志写入到内存中;
• Sync阶段,将内存中的二进制日志写入到磁盘,若队列中有多个事务,那么一次的fsync操作就完成了二进制日志的写入;
• Commit阶段,leader 根据顺序调用存储引擎层事务的提交,InnoDB存储引擎就来就支持group commit技术,因此修复了因为prepare_commit_mutex锁导致的问题。
当一组事务在commit阶段时,其它新事务可以在flush阶段,从而使用group commit不停地生效。参数binlog_max_flush_queue_time用来控制Flush阶段中等待的时间 ,即使之前的一组事务完成提交,当前一组也不会马上进入Sync阶段,而是至少要等待一段时间 ,该值的默认值是0,推荐也是0。
参考《MySQL技术内幕 -InnoDB存储引擎》整理,如侵权请联系vinin@163.com。