MySQL中InnoDB引擎如何实现事务的ACID特性

1,236 阅读7分钟

前言:

说到事务的ACID,各位一定都不陌生,它们指的是原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)

  • 原子性

    • 根据定义,原子性是指一个事务是一个不可分割的工作单位,其中的操作要么都做,要么都不做。要么全部提交,要么全部回滚。
  • 一致性

    • 事务中操作的数据及状态改变是一致的,即写入资料的结果必须完全符合预设的规则,不会因为出现 系统意外等原因导致状态的不一致。
  • 隔离性

    • 一个事务所操作的数据在提交之前,对其他事务的可见性设定(一般设定为不可见)
    • MySQL中的隔离级别有:读未提交(Read Uncommitted)、读已提交(Read committed)、可重复读(Repeatable Read)、串行化(Serializable)。
  • 持久性

    • 事务的持久性是指事务⼀旦提交后,数据库中的数据必须被永久的保存下来。即使服务器系统崩溃或服务器宕机等故障。只要数据库重新启动,那么⼀定能够将其恢复到事务成功结束后的状态。

MySQL中我们常用的默认引擎一般是InnoDB,接下来就来看看在InnoDB引擎中是如何实现事务的这四大特性的。

InnoDB引擎架构(MySQL5.7)

在这里插入图片描述

在说明InnoDB引擎是如何实现ACID之前,为了更好的理解,首先对InnoDB引擎架构中的某些部分有一些基本的认识。

  • 缓冲池(Buffer Pool)

    Buffer Pool中包含了磁盘中部分数据页的映射。
    当从数据库读取数据时,会先从Buffer Pool中读取数据,如果Buffer Pool中没有,则从磁盘读取后放入到Buffer Pool中。
    当向数据库写入数据时,会先写入到Buffer Pool中,Buffer Pool中更新的数据会定期刷新到磁盘中(此过程称为刷脏)。

  • 日志缓冲区(Log Buffer)

    当在MySQL中对InnoDB表进行更改时,这些更改首先存储在InnoDB日志缓冲区的内存中,然后写入通常称为重做日志(redo logs)的InnoDB日志文件中。

  • 双写机制缓存(DoubleWrite Buffer)

    Doublewrite Buffer是开在共享(系统)表空间的物理文件的 buffer,其大小是2MB.是一个一分为二的2MB空间。

    刷脏操作开始之时,先进行脏页**‘备份’**操作.将脏页数据写入 Doublewrite Buffer.

    将Doublewrite Buffer(顺序IO)写入磁盘文件中(共享表空间) 进行刷脏操作.(绝大多数是随机IO)

  • 回滚日志(Undo Log)

    Undo Log记录的是逻辑日志.记录的是事务过程中每条数据的变化版本和情况.

    在Innodb 磁盘架构中Undo Log 默认是开在共享(系统)表空间的物理文件的Buffer.

    在事务异常中断,或者主动(Rollback)回滚的过程中,Innodb基于 Undo Log进行数据撤销回滚,保证数据回归至事务开始状态.

  • 重做日志(Redo Log)

    Redo Log通常指的是物理日志,记录的是数据页的物理修改.并不记录行记录情况。(也就是只记录要做哪些修改,并不记录修改的完成情况)

    当数据库宕机重启的时候,会将重做日志中的内容恢复到数据库中。

原子性

Innodb事务的原子性保证,包含事务的提交机制和事务的回滚机制.

  • 提交机制

在这里插入图片描述

执行一个DML语句时,会执行如上图的操作

  1. 将修改后的数据存入缓冲池(Buffer Pool)中,等待刷脏。
  2. 在Log Buffer中写入重做日志(Redo Log),并将日志状态设置为Prepare。
  3. 返回MySQL服务层,记录BinLog日志。
  4. 将Log Buffer中的日志文件状态设为Commit,并等待日志文件存入盘中。

以上的等待刷脏、等待事务日志文件刷盘操作不是串行,而是交由后台进程并发处理

以上的事务提交过程称为XA的两阶段提交

  • 回滚机制

在Innodb引擎中事务的回滚机制是依托**回滚日志(Undo Log)**进行回滚数据的保证的.

在事务异常中断,或者主动(Rollback)回滚的过程中,Innodb基于 Undo Log进行数据撤销回滚,保证数据回归至事务开始状态.

隔离性

隔离性的实现主要依赖两大技术

  • LBCC(Lock Based Concurrency Control)

事务开始操作数据前,对其加锁,阻止其他事务对数据进行修改。
针对SQL的操作,使用当前读解决并发读写的隔离性问题。(当前读:读取到的数据是线程独占的、最新的数据)

  • MVCC(Multi Version Concurrency Control)

事务开始操作数据前,将数据在当下时间点进行一份数据快照(Snapshot)的备份,并用这个快照来提供给其他事务进行一致性读取 。

并发访问(读或写)数据库时,对正在事务内处理的数据做多版本的管理。
避免写操作的堵塞,从而引发读操作的并发阻塞问题,使用快照读解决并发读写的隔离性问题。

是基于undo日志进行多版本信息管理。

持久性

数据库IO的最小单位是页大小为16KB

操作系统IO的最小单位是页大小为4KB

所以针对数据库中的一页脏页,需要进行4次的磁盘IO操作。在操作中若突然断电,会出现页断裂现象。

基于事务的提交机制流程有可能出现三种场景.

  • 数据刷脏正常.一切正常提交
    Redo Log 循环记录.数据成功落盘.持久性得以保证

  • 数据刷脏的过程中出现的系统意外导致页断裂现象 (部分刷脏成功)
    针对页断裂情况,采用Double write机制进行保证页断裂数据的恢复.

  • 数据未出现页断裂现象,也没有刷脏成功

    MySQL通过Redo Log 进行数据的持久化即可

Double write机制详解

Doublewrite Buffer是开在共享(系统)表空间的物理文件的 buffer,其大小是2MB.

刷脏操作开始之时,先进行脏页**‘备份’**操作.将脏页数据写入 Doublewrite Buffer.

将Doublewrite Buffer(顺序IO)写入磁盘文件中(共享表空间) 进行刷脏操作.(绝大多数是随机IO)

Double Write机制其核心思想是: 在刷脏之前,建立脏页数据的副本.系统意外宕机造成页断裂的情况可通过脏页数据副本 (DoubleWrite Buffer)进行恢复.

一致性

从数据库层面,数据库通过原子性、隔离性、持久性来保证一致性。

也就是说ACID四大特性之中,一致性是目的,原子性、隔离性、持久性是手段,是为了保证一致性,数据库提供的手段。

数据库必须要实现AID三大特性,才有可能实现一致性。

例如,原子性无法保证,显然一致性也无法保证。

但是,如果在事务里故意写出违反制定规则的代码,一致性也还是无法保证的。(例如转账从本帐户扣了钱,没给其他账户加钱)

最后

感谢大家看到这里,文章有不足,欢迎大家指出;如果你觉得写得不错,那就给我一个赞吧。

也欢迎大家关注我的公众号:Java程序员聚集地,麦冬每天都会分享java相关技术文章或行业资讯,欢迎大家关注和转发文章!