2021-06-05 11:31  阅读(1509)
文章分类:MySQL 技术内幕 文章标签:MySQLMySQL 技术内幕MySQL 高级
©  原文作者:一直不懂 原文地址:https://blog.csdn.net/shenchaohao12321/category_8075653.html

若事务为非只读事务,则每次事务提交时需要进行一次 fsync操作,以此保证重做日志都已经写入磁盘。当数据库发生宕机时,可以通过重做日志进行恢复。虽然固态硬盘的出现提高了磁盘的性能,然而磁盘的fyne性能是有限的。为了提高磁盘 fsync的效率,当前数据库都提供了 group commit的功能,即一次 fsync可以刷新确保多个事务日志被写入文件。对于 InnoDB存储引擎来说,事务提交时会进行两个阶段的操作:

  1. 修改内存中事务对应的信息,并且将日志写入重做日志缓冲
  2. 调用 fsync将确保日志都从重做日志缓冲写入磁盘。

步骤2)相对步骤1)是一个较慢的过程,这是因为存储引擎需要与磁盘打交道。但当有事务进行这个过程时,其他事务可以进行步骤1)的操作,正在提交的事物完成提交操作后,再次进行步骤2)时,可以将多个事务的重做日志通过一次sync刷新到磁盘,这样就大大地减少了磁盘的压力,从而提高了数据库的整体性能。对于写入或更新较为频繁的操作, group commit的效果尤为明显。
然而在 InnoDB12版本之前,在开启二进制日志后, InnodB存储引擎的 group commit功能会失效,从而导致性能的下降。并且在线环境多使用 replication环境,因此二进制日志的选项基本都为开启状态,因此这个问题尤为显著。
导致这个问题的原因是在开启二进制日志后,为了保证存储引擎层中的事务和二进制日志的一致性,二者之间使用了两阶段事务,其步骤如下:

  1. 当事务提交时 InnoDB存储引擎进行 Prepare操作

  2. MySQL数据库上层写入二进制日志

  3. InnoDB存储引擎层将日志写入重做日志文件。

    • a)修改内存中事务对应的信息,并且将日志写人重做日志缓冲。
    • b)调用 fsync将确保日志都从重做日志缓冲写入磁盘。

一旦步骤2)中的操作完成,就确保了事务的提交,即使在执行步骤3)时数据库发生了宕机。此外需要注意的是,每个步骤都需要进行一次 fsync操作才能保证上下两层数据的一致性。步骤2)的 fsync由参数sync_ binlog控制,步骤3)的 fsync由参数innodb_fush_log_at_trx_commit控制。因此上述整个过程如图7-18所示。
202106051131134511.png

为了保证 MySQL数据库上层二进制日志的写入顺序和 InnoDB层的事务提交顺序一致, MySQL数据库内部使用了 prepare_commit_mutex这个锁。但是在启用这个锁之后,步骤3)中的步骤a)步不可以在其他事务执行步骤b)时进行,从而导致了 group commit失效。
然而,为什么需要保证 MySQL数据库上层二进制日志的写入顺序和 InnoDB层的事务提交顺序一致呢?这时因为备份及恢复的需要,例如通过工具 xtrabackup或者ibbackup进行备份,并用来建立 replication,如图7-19所示。
可以看到若通过在线备份进行数据库恢复来重新建立 replication,事务T的数据会产生丢失。因为在 InnoDB存储引擎层会检测事务T3在上下两层都完成了提交,不需要再进行恢复。因此通过锁 prepare_commit_mutex以串行的方式来保证顺序性,然而这会使 group commit无法生效,如图7-20所示。
202106051131139082.png202106051131145793.png

这个问题最早在2010年的 MySQL数据库大会中提出, Facebook MySQL技术组,Percona公司都提出过解决方案。最后由MariaDB数据库的开发人员 Kristian Nielsen完成了最终的“完美”解决方案。在这种情况下,不但 MySQL数据库上层的二进制日志写入是 group commit的, InnoDB存储引擎层也是 group commit的。此外还移除了原先的锁 prepare commit mutex,从而大大提高了数据库的整体性。 MySQL5.6采用了类似的实现方式,并将其称为 Binary Log Group Commit(BLGC)。
MySQL5.6BLGC的实现方式是将事务提交的过程分为几个步骤来完成,如图7-21所示。
202106051131152624.png

在 MySQL数据库上层进行提交时首先按顺序将其放入一个队列中,队列中的第一个事务称为 leader,其他事务称为 follower, leader控制着 follower的行为。BLGC的步骤分为以下三个阶段:

  1. Fush阶段,将每个事务的二进制日志写入内存中。
  2. Sync阶段,将内存中的二进制日志刷新到磁盘,若队列中有多个事务,那么仅次fsync操作就完成了二进制日志的写入,这就是BLGC。
  3. Commit阶段, leader根据顺序调用存储引擎层事务的提交, InnoDB存储引擎本就支持 group commit,因此修复了原先由于锁 prepare_commit_mutex导致 group commit失效的问题。

当有一组事务在进行 Commit阶段时,其他新事物可以进行 Flush阶段,从而使group commit不断生效。当然 group commit的效果由队列中事务的数量决定,若每次队列中仅有一个事务,那么可能效果和之前差不多,甚至会更差。但当提交的事务越多时, group commit的效果越明显,数据库性能的提升也就越大。
参数 binlog max flush queue time用来控制 Flush阶段中等待的时间,即使之前的一组事务完成提交,当前一组的事务也不马上进入Sync阶段,而是至少需要等待一段寸间。这样做的好处是 group commit的事务数量更多,然而这也可能会导致事务的响应时间变慢。该参数的默认值为0,且推荐设置依然为0。除非用户的 MySQL数据库系统中有着大量的连接(如100个连接),并且不断地在进行事务的写入或更新操作。


来源:https://blog.csdn.net/shenchaohao12321/category_8075653.html

点赞(0)
版权归原创作者所有,任何形式转载请联系作者; Java 技术驿站 >> 【MySQL技术内幕】49-事务的实现之group commit
上一篇
【MySQL技术内幕】48-事务的实现之purge
下一篇
【MySQL技术内幕】50-事务控制语句