2023-03-23  阅读(51)
原文作者:一直不懂 原文地址:https://blog.csdn.net/shenchaohao12321/article/details/82798275

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

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

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

  1. 当事务提交时 InnoDB存储引擎进行 Prepare操作
  2. MySQL数据库上层写入二进制日志
  3. InnoDB存储引擎层将日志写入重做日志文件。
  • a)修改内存中事务对应的信息,并且将日志写人重做日志缓冲。
  • b)调用 fsync将确保日志都从重做日志缓冲写入磁盘。

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

202303232333444571.png

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

202303232333451442.png

202303232333458963.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所示。

202303232333466534.png

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

  1. Flush阶段,将每个事务的二进制日志写入内存中。
  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个连接),并且不断地在进行事务的写入或更新操作。


Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。

它的内容包括:

  • 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
  • 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
  • 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
  • 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
  • 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
  • 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
  • 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
  • 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw

目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:

想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询

同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。

阅读全文