MySQL事务原子性、一致性和持久性以及回滚是如何实现的?

前言

《MySQL事务详解》一文中详细讲解了事务的概念,包括ACID特性,事务并发引起的问题,事务的四种隔离级别。
在事务的四种特性中,原子性、一致性、持久性通过数据库的redo log和undo log来完成,redo log称为重做日志,用来保证事务的原子性和持久性,undo log称为回滚日志,用来保证事务的一致性。事务的隔离性通过锁机制来实现。
这篇文章主要讲解redo log和undo log实现一致性、原子性和持久性。

redo 和 undo的作用都可以视为是一种恢复操作,redo恢复提交事务时修改的页操作,而undo回滚行记录到某个特定版本。因此两者记录的内容不同,redo通常是物理日志,记录的是页的物理修改操作。undo是逻辑日志,根据每行记录进行记录。

redo log

redo log称为重做日志,用来保证事务的原子性和持久性。重做日志即innodb存储引擎产生的日志,默认在mysql/data目录下面有两个文件ib_logfile0和ib_logfile1。当MySQL的实例和介质失败的时候,Innodb存储引擎就会使用重做日志文件进行恢复,保证事务的持久性。

重做日志由两部分组成:

  • 内存中的重做日志缓冲(redo log buffer),易丢失
  • 重做日志文件(redo log file),持久的

InnoDB存储引擎首先将重做日志信息放入重做日志缓冲区中,然后按一定的频率将其刷新到重做日志文件。该缓冲区不会设置得很大,因为一般每一秒刷新一次到日志文件中。

下图显示MySQL5.6下该缓冲区默认为8M:
在这里插入图片描述

一般会在一下三种情况刷新重做日志缓冲到重做日志磁盘文件中:

  • 主线程每一秒刷新一次重做日志缓冲到重做日志磁盘文件中(同步fsync方式)
  • 每个事务提交时由innodb_flush_log_at_trx_commit参数控制,其值取0表示事务提交时不触发写磁盘,而是等待主线程每秒的刷新操作,其值取1(默认值)表示同步写磁盘,其值取2表示异步写磁盘,即不能保证commit时一定会刷新重做日志缓冲到重做日志磁盘文件中,只是有这个动作发生

innodb_flush_log_at_trx_commit对事物提交性能的影响

由于innodb_flush_log_at_trx_commit默认值为1,所以默认情况下,当事务commit时,必须先将该事务的所有日志从重做日志缓冲中同步写入到重做日志文件进行持久化,该事物的commit操作才算完成。重做日志文件没有使用O_DIRECT选项,因此重做日志先写入文件系统缓存。为了确保重做日志写入磁盘,必须进行一次fsync操作,该操作的效率取决于磁盘的新能,进而影响了数据库的性能。

  • O_DIRECT: 任何读写操作都只在用户态地址空间和磁盘之间传送而不经过page cache。
  • O_SYNC: 只影响写操作,block当前写进程,先从用户态内存写入page cache, 再从page cache写入磁盘,然后才返回到用户进程。

下面比较innodb_flush_log_at_trx_commit对事物提交性能的影响:

先创建表t1和存储过程p_load:
在这里插入图片描述
在innodb_flush_log_at_trx_commit参数为1的情况下,执行命令CALL p_load(50 000),即向表t1插入50万行记录,并执行50万次fsync操作,所需时间接近2分钟:
在这里插入图片描述
在innodb_flush_log_at_trx_commit参数为0的情况下,所需时间大概为13秒:
在这里插入图片描述
innodb_flush_log_at_trx_commit设置不同值对于插入速度的影响:
在这里插入图片描述
因此,为了提高事务的提交性能,应该将大批量的数据放在事务中一次性commit,而且还有个好处是还可以使事务回滚时回到最开始的状态。

log block

在InnoDB存储引擎中,重做日志是以512字节(也是一个扇区的大小)进行存储的。这意味着重做日志缓存、重做日志文件都是以块的方式进行保存的,称之为重做日志块,每块的大小为512字节。若一个页中产生的重做日志数量大于512字节,则需要分割为多个重做日志块进程存储。

undo log

重做日志记录了事务的行为,可以很好的通过其对页进行“重做”操作,但是事务有时还需要回滚操作,这是就需要undo,将数据回滚到修改之前的样子。redo通常是物理日志,记录的是页的物理修改操作。undo是逻辑日志,根据每行记录进行记录。
上面说过redo存放子重做日志文件中,默认在mysql/data目录下面的两个文件ib_logfile0和ib_logfile1中。而undo存放在数据库内部的一个特殊段中,这个段称为undo段。

一个事务在修改当前一个页中某几条记录,同时还可能存在别的事务在对同一个页中另几条记录进行修改,因此,不能将一个页回滚到事务开始的样子,因为这样会影响到其他事务正在进行的工作。

undo log如何实现回滚?

用户执行了一个INSERT 10W条记录的数据,这个事务会导致分配一个新的段,即表空间会增大,但是事务ROLLBACK时,只是将插入操作的事务进行回滚,并没有缩减表的大小。因此,事务回滚实际做的是和之前相反的工作。对于INSERT,INSERT,对于DELETE,InnoDB存储引擎会完成一个INSERT操作,对于UPDATE,InnoDB存储引擎会完成一个相反的UPDATE操作。

这就实现了InnoDB存储引擎的回滚机制。

undo log的另一个作用

undo log的另一个作用是实现MVCC(多版本并发控制)。当用户读取被其他事务占用的记录时,可以通过undo读取之前的版本信息(即快照读),以此实现非锁定读。

此外,undo log会产生redo log,因为undo log也需要被记录下来,需要持久性的保护。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值