mysql的四种隔离级别

84 阅读6分钟

「这是我参与11月更文挑战的第30天,活动详情查看:2021最后一次更文挑战」。

1 事务的四个特征(ACID)

::: tip 事务的四个特征

  • 原子性(Atomicity)
  • 一致性(Consistency)
  • 隔离性(Isolation)
  • 持续性(Durability) :::

1.1 原子性(Atomicity)

化学中的原子指不可再分的基本微粒,数据库中原子性强调事务是一个不可分割的整体,事务开始后所有操作要么全部成功,要么全部失败,不可能停滞在中间某个环节。如果事务执行过程中出错就会回滚到事务开始前的状态,所有的操作就像没有发生一样不会对数据库有任何影响。

1.2 一致性(Consistency)

事务必须使数据库从一个一致性状态变换到另一个一致性状态,即一个事务执行之前和执行之后都必须处于一致性状态。拿转账来说,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还是5000,这就是事务的一致性。

1.3 隔离性(Isolation)

事务的隔离性基于原子性和一致性,每一个事务可以并发执行,但是他们互不干扰,但是也有可能不同的事务会操作同一个资源,这个时候为了保持隔离性会用到锁方案。当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离,比如A正在从一张银行卡中取钱,在A取钱的过程结束前,B不能向这张卡转入钱。

1.4 持续性(Durability)

当一个事务提交了之后那这个数据库状态就发生了改变,哪怕是提交后刚写入一半数据到数据库中,数据库宕机(死机)了,那当你下次重启的时候数据库也会根据提交日志进行回滚,最终将全部的数据写入。

2 四种隔离级别

2.1 Read Uncommitted(读取未提交内容)

在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用, 因为它的性能也不比其他级别好多少。读取未提交的数据,也被称之为脏读Dirty Read。脏读具体示例如下:

时间点事务A事务B
1开启事务
2开启事务
3查询数据为100条
4insert一条数据
5再查询,结果为101条
在时间点5,事务A再次查询数据时,事务B并没有提交事务,但是,新数据也被事务A查出来了。这就是脏读。

2.2 Read Committed(读取提交内容)

这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义: 一个事务只能看见已经提交事务所做的改变。这种隔离级别 也支持所谓的不可重复读Nonrepeatable Read, 因为同一事务的其他实例在该实例处理其间可能会有新的commit,所以同一select可能返回不同结果。

时间点事务A事务B
1开启事务
2开启事务
3查询数据为100条
4insert一条数据
5查询数据为100条
6提交事务
7再查询,结果为101条

       我们可以看到,事务B在提交事务之前,事务A的两次查询结果是一致的。事务B提交事务以后,事务A再次查询,查询到了新增的这条数据。在事务A中,多次查询的结果不一致,这就是我们说的不可重复读

2.3 Repeatable Read(可重读)

这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。 不过理论上,这会导致另一个棘手的问题:幻读Phantom Read。简单的说,幻读指当用户读取某一范围的数据行时, 另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的"幻影" 行。

       上面这一段是MySQL官方给出的解释,听着云里雾里。"可重读"这种隔离级别解决了上面例子中的问题,保证了同一事务内,多次查询的结果是一致的。也就是说,事务B插入数据提交事务后,事务A的查询结果也是100条,因为事务A在开启事务时,事务B插入的数据还没有提交。

       但是,这又引出了另外一个情况,"幻读"。这个幻读我之前理解是有问题的,在面试时,被对方一顿质疑。现在我们就看看幻读的正确理解:

时间点事务A事务B
1开启事务
2开启事务
3查询数据"张三",不存在
4插入数据"张三"
5提交事务
6查询数据"张三",不存在
7插入数据"张三",不成功

      事务A查询"张三",查询不到,插入又不成功,"张三"这条数据就像幻觉一样出现。 这就是所谓的"幻读"。网上对"幻读"还是其他的解释,都是错误的。 比如像"幻读"和"不可重复读"是一样,只不过"幻读"是针对数据的个数。这些理解都是错误的

2.4 Serializable(可串行化)

这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。 在这个级别,可能导致大量的超时现象和锁竞争。这种隔离级别很少使用,没有做过多的研究。

3 出现的问题

这四种隔离级别采取不同的锁类型来实现,若读取的是同一个数据的话,就容易发生问题。例如:

3.1 脏读(Dirty Read)

某个事务已更新一份数据,另一个事务在此时读取了同一份数据,由于某些原因,前一个RollBack了操作,则后一个事务所读取的数据就会是不正确的。

3.2 不可重复读(Non-repeatable read)

在一个事务的两次查询之中数据不一致,这可能是两次查询过程中间插入了一个事务更新的原有的数据。

3.3 幻读(Phantom Read)

在一个事务的两次查询中数据笔数不一致,例如有一个事务查询了几列(Row)数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的。

3.4 总结图

四种隔离级别产生问题汇总