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 事务的隔离级别
针对事务并发给数据库带来的三个读一致性的问题,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 存储引擎支持的隔离级别
在 MySQL InnoDB 存储引擎里,不需要使用串行化的隔离级别去解决所有问题,下面我们来通过一张表格来看一下 InnoDB 对数据库事务隔离级别的支持情况:
从上面的表可以看出 InnoDB 支持的四个隔离级别和 SQL92 标准定义的基本一致,隔离级别越高,事务的并发度就越低。唯一的区别是 InnoDB 存储引擎在可重复读(RR) 的级别就解决了幻读的问题。这个也是 InnoDB 默认使用 RR 作为事务隔离级别的原因,既保证了数据的一致性,又支持较高的并发度。