乐趣区

关于mysql:MySQL事务的ACID是如何实现的

MySQL 中默认采纳的是主动提交(autocommit)模式,在主动提交模式下,如果没有 start transaction 显式地开始一个事务,那么每个 sql 语句都会被当做一个事务执行提交操作。

一、ACID 个性

ACID 是掂量事务的四个个性:

  • 原子性(Atomicity,或称不可分割性)
  • 一致性(Consistency)
  • 隔离性(Isolation)
  • 持久性(Durability)

二、原子性

1、定义

原子性是指一个事务是一个不可分割的工作单位,其中的操作要么都做,要么都不做;如果事务中一个 sql 语句执行失败,则已执行的语句也必须回滚,数据库退回到事务前的状态。

2、实现原理:undo log

实现原子性的要害,是当事务回滚时可能撤销所有曾经胜利执行的 sql 语句。InnoDB 实现回滚,靠的是 undo log:当事务对数据库进行批改时,InnoDB 会生成对应的 undo log;如果事务执行失败或调用了 rollback,导致事务须要回滚,便能够利用 undo log 中的信息将数据回滚到批改之前的样子。
undo log 属于逻辑日志,它记录的是 sql 执行相干的信息。当产生回滚时,InnoDB 会依据 undo log 的内容做与之前相同的工作:对于每个 insert,回滚时会执行 delete;对于每个 delete,回滚时会执行 insert;对于每个 update,回滚时会执行一个相同的 update,把数据改回去。

三、持久性

1、定义

持久性是指事务一旦提交,它对数据库的扭转就应该是永久性的。接下来的其余操作或故障不应该对其有任何影响。

2、实现原理:redo log

上面先聊一下 redo log 存在的背景。
InnoDB 作为 MySQL 的存储引擎,数据是寄存在磁盘中的,但如果每次读写数据都须要磁盘 IO,效率会很低。为此,InnoDB 提供了缓存(Buffer Pool),Buffer Pool 中蕴含了磁盘中局部数据页的映射,作为拜访数据库的缓冲:当从数据库读取数据时,会首先从 Buffer Pool 中读取,如果 Buffer Pool 中没有,则从磁盘读取后放入 Buffer Pool;当向数据库写入数据时,会首先写入 Buffer Pool,Buffer Pool 中批改的数据会定期刷新到磁盘中

Buffer Pool 的应用大大提高了读写数据的效率,然而也带了新的问题:如果 MySQL 宕机,而此时 Buffer Pool 中批改的数据还没有刷新到磁盘,就会导致数据的失落,事务的持久性无奈保障。

于是,redo log 被引入来解决这个问题:当数据批改时,除了批改 Buffer Pool 中的数据,还会在 redo log 记录这次操作;当事务提交时,会调用 fsync 接口对 redo log 进行刷盘。

所有批改先写入日志 redo log,再更新到 Buffer Pool,如果 MySQL 宕机,重启时能够读取 redo log 中的数据,保障了数据不会因 MySQL 宕机而失落,从而满足了持久性要求。

既然 redo log 也须要在事务提交时将日志写入磁盘,为什么它比间接将 Buffer Pool 中批改的数据写入磁盘 (即刷脏) 要快呢?次要有以下两方面的起因:
(1)刷脏是随机 IO,因为每次批改的数据地位随机,但写 redo log 是追加操作,属于程序 IO。
(2)刷脏是以数据页(Page)为单位的,MySQL 默认页大小是 16KB,一个 Page 上一个小批改都要整页写入;而 redo log 中只蕴含真正须要写入的局部,有效 IO 大大减少。

3、redo log 与 binlog

在 MySQL 中还存在 binlog(二进制日志)也能够记录写操作并用于数据的复原,但二者是有着基本的不同的:
(1)作用不同:redo log 是用于 crash recovery 的,保障 MySQL 宕机也不会影响持久性;binlog 是用于 point-in-time recovery 的,保障服务器能够基于工夫点复原数据,此外 binlog 还用于主从复制。
(2)档次不同:redo log 是 InnoDB 存储引擎实现的,而 binlog 是 MySQL 的服务器层
(3)内容不同:redo log 是物理日志,内容基于磁盘的 Page;binlog 是逻辑日志,内容是一条条 sql。
(4)写入机会不同:binlog 在事务提交时写入;redo log 的写入机会绝对多元

四、隔离性

1、定义

隔离性是指,事务外部的操作与其余事务是隔离的,并发执行的各个事务之间不能相互烦扰。
隔离性的探讨,次要能够分为两个方面:
(一个事务)写操作对 (另一个事务) 写操作的影响:锁机制保障隔离性
(一个事务) 写操作对 (另一个事务) 读操作的影响:MVCC 保障隔离性

2、锁机制

锁机制的基本原理能够概括为:事务在批改数据之前,须要先取得相应的锁;取得锁之后,事务便能够批改数据;该事务操作期间,这部分数据是锁定的,其余事务如果须要批改数据,须要期待以后事务提交或回滚后开释锁
MySQL 中不同的存储引擎反对的锁是不一样的,例如 MyIsam 只反对表锁,而 InnoDB 同时反对表锁和行锁,且出于性能思考,绝大多数状况下应用的都是行锁。

3、脏读、不可反复读和幻读

首先来看并发状况下,读操作可能存在的三类问题:
(1)脏读:以后事务 (A) 中能够读到其余事务 (B) 未提交的数据(脏数据),这种景象是脏读。
(2)不可反复读:在事务 A 中先后两次读取同一个数据,两次读取的后果不一样,这种景象称为不可反复读。
(3)幻读:在事务 A 中依照某个条件先后两次查询数据库,两次查问后果的条数不同,这种景象称为幻读。

4、事务隔离级别

5、MVCC

MVCC 的特点:在同一时刻,不同的事务读取到的数据可能是不同的(即多版本)
MVCC 最大的长处是读不加锁,因而读写不抵触,并发性能好。

InnoDB 实现 MVCC,多个版本的数据能够共存,次要是依附数据的暗藏列 (也能够称之为标记位) 和 undo log。其中数据的暗藏列包含了该行数据的版本号、删除工夫、指向 undo log 的指针等等;当读取数据时,MySQL 能够通过暗藏列判断是否须要回滚并找到回滚须要的 undo log,从而实现 MVCC;

五、一致性

1、定义

一致性是指事务执行完结后,数据库的完整性束缚没有被毁坏,事务执行的前后都是非法的数据状态。数据库的完整性束缚包含但不限于:实体完整性(如行的主键存在且惟一)、列完整性(如字段的类型、大小、长度要符合要求)、外键束缚、用户自定义完整性(如转账前后,两个账户余额的和应该不变)。

2、实现

能够说,一致性是事务谋求的最终目标:后面提到的原子性、持久性和隔离性,都是为了保障数据库状态的一致性。此外,除了数据库层面的保障,一致性的实现也须要利用层面进行保障,例如如果转账操作只扣除转账者的余额,而没有减少接收者的余额,无论数据库实现的如许完满,也无奈保障状态的统一。

参考资料:

https://cloud.tencent.com/dev…
https://xiaolincoding.com/mys…

退出移动版