假设没有事务的隔离性,当两个事务并发地去操作数据库的表或者行的时候,那么会产生什么问题?

1

并发给数据库带来的问题

数据库并发问题的挑战与解决之道_数据库

1、读脏数据

A 事务修改了一个数据(假设 A 从 1 改为 2),B 事务读取 A 事务修改后的数据(B 读取到了 2),但由于某种原因 A 事务撤销了事务(这个数据回滚到 1),此时导致 B 事务读取到的数据不正确(B 认为数据是 2),这就表示 B 事务读取到了脏数据,一般称为脏读。

2、不可重复读

A 事务读取数据后(假设 A 读到了 1),B 事务执行更新操作(B 把 1 改成了 2),然后 B 事务提交(此时数据的值为 2);然后 A 事务再次读取数据(A 读到了 2),也就是说 A 事务两次读取操作得到了2个结果(第一次是 1,第二次是 2)。说明在一个事务里对同一个数据读取,得到的值不一致,后面的读操作获得了其他它事务提交的结果,这就表示 A 事务不可重复度。

3、幻读

A 事务读取价格小于 10 的产品数据(假设 A 读到了 100 条数据),B 事务往产品表里插了一条价格小于 10 的产品,然后 B 事务提交(此时价格小于 10 的产品有 101 条);然后 A 事务再次读取价格小于 10 的产品数据(A 读到了 101 条数据),也就是说 A 事务读取到了 B 事务新增的记录(第一次是 100 条,第二次是 101 条)。这就表示 A 事务发生了幻读。

幻读与不可重复读的区别:不可重复读是由其他事务的修改操作引发的读不一致,幻读是由于其他事务新增或者删除记录引发的读不一致。从结果上看,不可重复读和幻读差不多。但从控制的角度来看, 两者的区别就比较大。对于前者, 只需要锁住满足条件的记录。对于后者, 要锁住满足条件及其相近的记录。

事务并发带来的三大问题,无论是脏读,还是不可重复读,或者是幻读,它们都是数据库的读一致性的问题,都是在一个事务里面前后两次读取出现了不一致的情况。

2

MySQL 事务的隔离级别

数据库并发问题的挑战与解决之道_数据库_02

针对事务并发给数据库带来的三个读一致性的问题,SQL92 标准为各个数据库厂商提供一定的事务隔离级别,来解决事务并发的问题。

1、读未提交(Read Uncommitted)

第一个隔离级别:读未提交(Read Uncommitted),又叫做 RU,一个事务可以读取到其他事务未提交的数据,会出现脏读,它没有解决任何的问题。

2、读提交(Read Committed)

第二个隔离级别:读提交(Read Committed),又叫做 RC,也就是一个事务只能读取到其他事务已提交的数据,不能读取到其他事务未提交的数据,它解决了脏读的问题,没有解决不可重复读和幻读的问题。

3、可重复读(Repeatable Read)

第三个隔离级别:可重复读(repeatable Read),又叫做 RR,它解决了不可重复读的问题,也就是在同一个事务里面多次读取同样的数据结果是一样的,没有解决幻读的问题。

4、串行化(Serializable)

第四个隔离级别:串行化(Serializable),在这个隔离级别里面,所有的事务都是串行执行的,也就是对数据的操作需要排队执行,已经不存在事务的并发操作了,所以它解决了所有的问题。

以上的四个隔离级别是 SQL92 的标准,但是不同的数据库厂商或者存储引擎的实现有一定的差异。

3

InnoDB 存储引擎支持的隔离级别

数据库并发问题的挑战与解决之道_数据_03

在 MySQL InnoDB 存储引擎里,不需要使用串行化的隔离级别去解决所有问题,下面我们来通过一张表格来看一下 InnoDB 对数据库事务隔离级别的支持情况:

数据库并发问题的挑战与解决之道_数据库_04

从上面的表可以看出 InnoDB 支持的四个隔离级别和 SQL92 标准定义的基本一致,隔离级别越高,事务的并发度就越低。唯一的区别是 InnoDB 存储引擎在可重复读(RR) 的级别就解决了幻读的问题。这个也是 InnoDB 默认使用 RR 作为事务隔离级别的原因,既保证了数据的一致性,又支持较高的并发度。

后面将为大家介绍 InnoDB 存储引擎的隔离级别是如何实现的。