MySQL 事务知识整理

最近在招 DBA ,在面试的过程中,通过学习也加深了对事务这一概念的理解,下面做个简单的整理。

1、什么是事务

事务是由一步或几步数据库操作序列组成逻辑执行单元,这系列操作要么全部执行,要么全部放弃执行。

事务是在引擎层实现的,在 MySQL 中只有使用了 Innodb 引擎的数据库或表才支持事务。MyISAM 引擎就不支持事务。

2、事务的作用

事务处理可以用来维护数据库的完整性,保证成批的 SQL 语句要么全部执行,要么全部不执行。

事务用来管理 insert, update, delete 语句。

3、事务的 ACID 特性

原子性 Atomicity
又叫不可分割性,要么全完成,要么全不完成。就像原子是物质的最小组成单位一样,原子内的各种操作是一个整体。如果执行过程中发生了错误,就会回滚到最初状态。

一致性 Consistency
事务开始前和事务结束后,数据库的完整性不会被破坏,这表明操作的数据必须符合所有规范。

隔离性 Isolation
又叫独立性,数据库允许多个事务同时执行,隔离性可以防止多个事务并发执行导致数据的不一致。

持久性 Durability
事务处理结束后,对数据的修改是永久的,即使系统故障也不会丢失。

4、事务的隔离级别

下面的 4 种隔离级别,越往下,效率越低,并发越低,安全性越高。


1)读未提交(Read Uncommitte,RU)

  • 定义:所有事务都可以看到其他未提交事务的执行结果。
  • 说人话:一个事务里可以读到别人还没有提交的变更。别人可能处理成功,也可以回滚,导致你读到的数据不一定是对的。
  • 优点:效率最高,并发最强
  • 缺点:可能导致脏读、幻读、非重复读,安全性最低


2)读提交(Read Committed,RC,Oracle 默认的隔离级别)

  • 定义:事务在提交之前,做的变更都无法被其它事务看见,但是事务本身可以看到数据库的变更。
  • 说人话:一个事务里可以读到别人已经提交的变更,即读取最新一份快照数据。如果一个事务里有两次读,两次读的期间别人完成了操作,就会导致两次读的虽然都是真的(不是脏读),但是结果不一样(非重复读)。
  • 优点:效率稍高,并发稍强
  • 缺点:可能导致幻读、非重复读,安全性较低


3)可重复读(Repeatable Read,RR,MySQL 默认的隔离级别)

  • 定义:事务总是只能看到在启动的那个时刻,数据库的状态,即在事务未提交之前,自己做的变更别的事务看不见,数据库中的变更自己也看不见。
  • 说人话:一个事务里,不会读取别的事务提交的变更,而是会读取事务开始时的快照数据
  • 优点:不会导致脏读、非重复读
  • 缺点:当快照读和当前读混用时,会导致幻读

MySQL Innodb 引擎是如何避免幻读的
MySQL Innodb 引擎做了特殊实现,即加锁。在 RR 级别下,如果查询条件使用了唯一索引,就会对读取到的记录加上行锁;如果查询条件是一个范围查询,那么就会对读取到的记录加上 gap 锁(间隙锁)或者 next-key 锁 ( 行锁 + 间隙锁),避免幻读。
一个事务里,不会读取别的事务提交的变更,而是会读取事务开始时的快照数据。

很多人无法区分幻读与不可重复读,这里简单做个对比。

幻读与不可重复读的区别
不可重复读:在一个事务中多次读取同一个数据时,结果出现不一致。


幻读:在一个事务中使用相同的 SQL 两次读取,第二次读取到了其他事务新插入的行。


4)串行化(Serializable)

  • 定义:事务对某一行的操作会加锁,“写”会加“写锁”,“读”会加“读锁”,在锁释放掉之前,其它的事务都无法都这一行的记录进行操作。必须等之前的事务执行完毕,释放锁。后面的事务又会重新加锁。
  • 说人话:事务执行的时候,会将使用的数据范围锁起来不让别人用,所有事务排队执行。
  • 优点:安全性最高
  • 缺点:效率最低、并发最低


总结一下:

隔离级别 是否允许脏读 是否允许非重复读 是否允许幻读
读未提交(RU)
读提交(RC) X
可重复读(RR) X X √(MySQL InnoDB 是 X)
串行化 X X X

隔离的越严实,效率就越低,并发越低,安全性越高。

5、事务的实现原理

事务的隔离性是通过锁实现的,事务的一致性和持久性是通过 Redo Log (重做日志)实现的,事务的原子性是通过 Undo Log (回滚日志)实现的。

5.1、隔离性的实现

事务的实现是基于数据库的存储引擎。不同的存储引擎对事务的支持程度不一样。MySQL 中支持事务的存储引擎有 InnoDB 和 NDB。

InnoDB 是 MySQL 默认的存储引擎,默认的隔离级别是 RR(可重复读),并且在 RR 的隔离级别下更进一步:

  1. 用多版本并发控制(MVCC )解决不可重复读问题:MVCC 允许一个行记录可能有不止一个快照数据,因此使用快照读就可以实现可重复读。
  2. 用行锁 + 间隙锁解决幻读问题:行锁避免了数据的 update/delete,间隙锁避免了数据的 insert 。

因此 InnoDB 的 RR 隔离级别其实也实现了串行化级别的效果,而且保留了比较好的并发性能。

5.2、一致性和持久性的实现

在 InnoDB 的存储引擎中,事务通过 Redo Log 和 InnoDB 存储引擎的日志缓冲( InnoDB Log Buffer ) 实现一致性和持久性。

  • 事务开启时:事务中的操作,都会先写入存储引擎的日志缓冲中,在事务提交之前,这些缓冲的日志都需要提前刷新到磁盘上持久化,这就是 DBA 们口中常说的“日志先行”(Write-Ahead Logging)。
  • 事务提交之后:在 Buffer Pool 中映射的数据文件才会慢慢刷新到磁盘。此时如果数据库崩溃或者宕机,那么当系统重启进行恢复时,就可以根据 Redo Log 中记录的日志,把数据库恢复到崩溃前的一个状态。未完成的事务,可以继续提交,也可以选择回滚,这基于恢复的策略而定。

5.3、原子性的实现

在事务执行的过程中,除了记录 Redo Log,还会记录 Undo Log 。

Undo Log 主要为事务的回滚服务。Undo Log 记录了数据在每个操作前的状态,如果事务执行过程中需要回滚,就可以根据 Undo Log 进行回滚操作。

单个事务的回滚,只会回滚当前事务做的操作,并不会影响到其他的事务做的操作。

6、MySQL 事务控制

MySQL 里的事务控制语句有:

  • 开启事务:beginstart transaction
    tables 被执行。
  • 提交事务:commitcommit work ,使对数据库进行的所有修改成为永久性的。
  • 回滚事务:rollbackrollback work,会结束用户的事务,并撤销正在进行的所有未提交的修改。注意,所有的 DDL 语句是不能回滚的,并且部分的 DDL 语句会造成隐式的提交。
  • 创建保存点:savepoint identifier,一个事务中可以有多个 savepoint;
  • 删除保存点:release savepoint identifier,当没有指定的保存点时,执行该语句会抛出一个异常
  • 回滚事务到保存点:rollback to identifier
  • 设置事务的隔离级别:set transaction
  • 修改自动提交模式:set autocommit=0 代表禁止自动提交,=1代表开启自动提交。

你可能感兴趣的:(MySQL 事务知识整理)