InnoDB(7.3):事务——purge操作与group commit

前面提到过,事务的delte和update操作是不直接删除原有数据的,都是采用标记删除,即在undo log页标记为删除,但在实际的B+树索引还是有该记录的,真正删除这行记录的操作其实被时延了,最终是在Purge操作中完成的

Purge

purge操作其实是为了支持MySQL的MVCC,所以记录不能在事务提交时就立即进行处理,因为可能其他事务正在使用这一行,故InnoDB存储引擎需要保存记录之前的版本。

若该行记录已不被任何其他事务引用,那么就可以进行真正的delete操作,所以可以理解成,purge操作并不是处理当前事务的delete操作,而是去清除之前的delete和update操作,而实际执行的操作只有delete,清理之前行记录的版本(前面提到过,更新的操作分为两种,一种是对主键更新,删除再新增,另一种是不对主键更新,不用删除也不用新增,所以实际操作只有delete【新增的操作是给Insert undo log去做的】)

purge实现清理undo页

undo页是存储undo log的地方,前面提到过,undo页里面是允许不同事务的undo log同时存在的,那么要去清理undo页的时候需要怎么去清除?

清除的话,肯定是要undo页里面的所有事务的undo log都不再需要才会去清除,问题就是要怎么去避免尽量离散判断undo页里面的undo log是否已经不需要(因为在数据库内,只可以去判断undo log是否有被占用)

InnoDB存储引擎上还有一个history链表,它根据事务提交的顺序,将undo log进行链接,采用的是头插法,也就是先提交的事务总是在尾端

该链表结构如下
在这里插入图片描述
trx4~trx1代表4个事务,而其对应的下面,就是事务使用的undo log,假设undo log4和undo log3位于Undo page1中,而另外两个在undo page 2中,注意的是,在history链表里面都是已提交的事务,代表已经不需要使用undo页了,等待被清除。

InnoDB存储引擎会从history中先选出第一个事务(即trx4),通过trx4的undo log4找到undo page 1,然后去判断undo page 1里面的所有undo log是否还在被占用,如果都不被占用,就代表该undo page 1是可以被清理的,也就是可以被重用,这样设计就避免了离散去判断undo page,大量减少了随机读操作,提高了purge效率

purge的相关参数

SHOW VARIABLES LIKE "innodb_%purge%";

在这里插入图片描述

  • innodb_purge_batch_size:是设置每次purge操作需要清理的undo page数量,这个值越大,每次回收的undo page数量也就越多,可提供使用的undo page数量也就越多,减少了磁盘存储空间与分配的开销,但也是会有一个弊端就是,每次purge处理都要处理更多的undo page,会占用更多的CPU和磁盘IO,使性能下降

  • innodb_max_purge_lag:当InnoDB存储引擎压力过大时,并不能高效地去进行purge操作,那么就会导致history链表的长度越来越大,该参数就是用来控制当history链表的长度达到某一长度时,就会主动去延缓DML操作,去进行purge操作,该参数默认为0,也就是不对history链表作任何限制,有事务提交了就要去进行purge

    • 延缓的的算法为 d e l a y = ( ( l e n g t h ( h i s t o r y ) − 该 参 数 ) ∗ 10 ) − 5 delay=((length(history)-该参数)*10)-5 delay=((length(history))10)5,单位为毫秒
    • 要注意的是,这个延缓针对的是行,而不是整个DML操作(整个DML操作可能包括多行),每一行都要重新计算延缓
  • innodb_max_purge_lag_delay:控制delay的最大毫秒数,当计算出来的delay大于这个参数时,就会将delay改为这个参数

group commit

若事务为非只读事务,那么每次事务提交时,都需要进行一次fsync操作(同步写入重做日志,先写入重做日志,然后再持久化数据),以确保重做日志都已经被写入磁盘,当事务提交时发生宕机,可以通过重做日志进行恢复(其实本质上,宕机还是可能会发生数据丢失的,只不过持久化重做日志的时间比持久化数据快,减少了丢失数据的概率),然而fsync的操作性能是有限的。

InnoDB提供了group commit,即一次fsync可以刷新确保多个事务日志被写入文件,对于InnoDB存储引擎来说,事务提交时会进行两个阶段的操作**(让一些提交的事务,正在将日志写入重做日志缓冲,但还没有写入磁盘,那么这部分的重做日志缓冲也可以跟随已提交的事务的重做日志缓冲,一并刷新进磁盘)**

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

将多个事务的重做日志通过一次fsync刷新到磁盘,这样就大大地减少了磁盘的压力,从而提高了数据库的整体性能,尤其是对于写入或更新较为频繁的操作,group commit的效果尤为明显

拓展:InnoDB1.2版本之前,group commit的不足

之前,如果开启了bin log(bin log属于SQL层),那么InnoDB的group commit是会失效的,因为要保证bin log与重做日志的一致性。

我们先看看开启了bin log之后,事务提交的流程

  1. 当事务提交时,InnoDB存储引擎进行Prepare操作
  2. MySQL数据库上层写入bin log
  3. InnoDB存储引擎层写入重做日志
    1. 修改内存中对应的信息,并且将日志写入重做日志缓冲
    2. 调用fsync同步将重做日志缓冲刷新进磁盘

可以看到,如果当事务A已经写入了bin log,然后正在写入重做日志缓冲时,事务B到了刷新重做日志缓冲,那么就会把事务A的部分重做日志缓冲刷新进去,事务A就会形成一种重做日志与bin log不一致的现象(bin log的数据多与重做日志的数据)

所有,为了解决这个问题

旧版本的MySQL是使用了锁(prepare_commit_mutex)去解决

这个锁的作用是:当有事务正在写入重做日志缓冲时,其他事务是不可以刷新重做日志缓冲的。这样就保证了必须等待事务重做日志缓冲完整时,才会进行刷新重做日志,那么就保证了重做日志与Bin log的一致性

而且在主从的情况下,MySQL都是会开启bin log的,对于生产环境是不友好的,因为很明显就降低了性能。

BLGC

为了解决上面的问题,MySQL5.6之后,采用了Binary Log Group Commit。

总体思路为:MySQL数据层上层的bin log写入时使用group commit,InnoDB引擎层也是Group comit

这样的设计,就可以保证一致性,然后还移除了原先的prepare_commit_mutex锁,极大地提高了数据库的性能

BLGC的实现步骤

首先,MySQL数据库上层(SQL层)会将提交的事务按照提交顺序将其放入一个队列中,队列中的第一个事务称为leader,其他事务称为follower,Leader会控制follwer的行为。

BLGC有三个阶段

  1. Flush阶段:将队列中的每个事务的二进制日志写入内存中
  2. Sync阶段:将内存中的bin log刷新到磁盘中,若队列中有多个事务,那么仅仅一次fsync操作就可以完成二进制日志的写入(这就是BLGC的核心操作)
  3. Commit阶段:leader根据顺序调用存储引擎层事务的提交,调用InnoDB的group commit

总的来说,就是按组去进行提交事务,按组去写入bin log,按组去group commit,由于都是同一个组,所以保证了bin log与重做日志的一致性。

当然,这样的操作也是有弊端的,由于是一组一组进行处理,所以在一组事务在Flush阶段可能去停留一段时间,要去等更多的事务进行队列中,如果每次队列中只有一个事务,可能会导致性能比之前的加锁更慢,所以效果也是由队列中的事务量来决定的,但由于需要等待,所以事务的响应时间会变慢

bin_log_max_flush_queue_time控制着事务队列在Flush阶段等待的时间(默认为0秒)

SHOW VARIABLES LIKE "binlog_max_flush%";

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值