数据库事务的四种隔离级别

1. 引言

“读现象” 是在多个事务并发执行时,在读取数据方面可能碰到的状况,了解它们有助于理解各隔离级别的含义,其中包括脏读、不可重复读和幻读。

2. 事务隔离级别

在数据库中,事务要满足ACID的四个性质,即原子性、一致性、持久性以及隔离性。在ACID四个属性中,隔离性是最常放松的一个,可以在数据操作过程中利用数据库的锁机制或者多版本并发控制机制获取更高的隔离等级。

但是,随着数据库隔离级别的提高,数据的并发能力也会有所下降。所以,如何在并发性和隔离性之间做一个很好的权衡就成了一个至关重要的问题。

2.1 事务四种隔离级别

MySQL 中事务的隔离级别一共分为四种,分别如下:

  • 读未提交(READ UNCOMMITTED)
  • 读已提交(READ COMMITTED)
  • 可重复读(REPEATABLE READ) 默认
  • 序列化(SERIALIZABLE)

四种不同的隔离级别含义分别如下:

  • 读未提交(READ UNCOMMITTED)

READ UNCOMMITTED 提供了事务之间最小限度的隔离。除了容易产生虚幻的读操作和不能重复的读操作外,处于这个隔离级的事务可以读到其他事务还没有提交的数据,如果这个事务使用其他事务不提交的变化作为计算的基础,然后那些未提交的变化被它们的父事务撤销,这就导致了大量的数据变化。

  • 读已提交(READ COMMITTED)

处于 READ COMMITTED 级别的事务可以看到其他事务对数据的修改。也就是说,在事务处理期间,如果其他事务修改了相应的表,那么同一个事务的多个 SELECT 语句可能返回不同的结果。在一个事务内,能看到别的事务提交的数据

  • 可重复读(REPEATABLE READ) 默认

在可重复读在这一隔离级别上,事务不会被看成是一个序列。不过,当前正在执行事务的变化仍然不能被外部看到,也就是说,如果用户在另外一个事务中执行同条 SELECT 语句数次,结果总是相同的。(因为正在执行的事务所产生的数据变化不能被外部看到)。

别的事务提交的数据,也看不到

  • 序列化(SERIALIZABLE)

如果隔离级别为序列化,则用户之间通过一个接一个顺序地执行当前的事务,这种隔离级别提供了事务之间最大限度的隔离。

2.2 查看隔离级别

通过如下 SQL 可以查看数据库实例默认的全局隔离级别当前 session 的隔离级别

MySQL8 之前使用如下命令查看 MySQL 隔离级别:

SELECT @@GLOBAL.tx_isolation, @@tx_isolation;

全局隔离级别和当前会话隔离级别皆是如此。

MySQL8 开始,通过如下命令查看 MySQL 默认隔离级别

SELECT @@GLOBAL.transaction_isolation, @@transaction_isolation;

就是关键字变了,其他都一样。

通过如下命令可以修改隔离级别(建议开发者在修改时修改当前 session 隔离级别即可,不用修改全局的隔离级别):

SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

上面这条 SQL 表示将当前 session 的数据库隔离级别设置为 READ UNCOMMITTED,设置成功后,再次查询隔离级别,发现当前 session 的隔离级别已经变了。

注意,如果只是修改了当前 session 的隔离级别,则换一个 session 之后,隔离级别又会恢复到默认的隔离级别,所以我们测试时,修改当前 session 的隔离级别即可。

3. 脏读/幻读/不可重复读

下面,通过例子来理解数据库的脏读、幻读、不可重复读。假如你们小组入职了一位小白美女新同事,新同事来的第一件事情,就是要熟悉项目代码,于是你就很热情的帮她开了SVN账号和密码,把代码帮她拉下来,然后让她熟悉熟悉……

3.1 脏读

第一种读现象叫做脏读,顾名思义,就是读到了脏数据,即无效数据。

脏读。是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交(commit)到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是脏数据,依据脏数据所作的操作可能是不正确的。

打个比方:什么情况下会出现脏读呢?

就是你在你本地修改了一个PayExecutor这个类的代码,但你还没提交。新同事为了更快的知道这个类被你改成什么样,她跑到你的电脑前面阅读了你修改的代码。这就是脏读了。因为你没有提交代码,说明你随时可以撤销刚刚的修改,这时新同事之前读到的数据就是脏数据了。这种情况下,多个开发者之间的并发性很高,几乎没有任何阻塞,新同事想知道代码最新的是什么样,她就读到了最新的代码。但是,这个代码你还没有提交,这就是个脏数据,你们之间的隔离性很差。

脏读:读到别的事务没有提交的数据。

3.2 不可重复读

为了解决脏读问题,你们决定提高一个隔离性,规定:你在修改代码的时候,不允许新同事跑到你的电脑前面读代码。她只能读取到你提交后的代码。

当你需要修改PayExecutor这个类的时候,你需要告诉新同事让她等一下,等你提交完代码,她再读。这就有效的解决了脏读,因为新同事读到的代码全部都是你已经提交的代码。

但是,提高了你们之间的隔离性,并发性就降低了一些。因为她要等你提交代码后才能阅读。

好的,你们现在已经提高了一点隔离级别,使得脏读现象没有了,但是并没有办法避免以下现象:

新同事在阅读PayExecutor的代码时,代码中定义了一个LOGGER常量。这时你修改了代码,把这个常量删除了,然后提交了。新同事更新了代码继续阅读,但是她却发现LOGGER这个常量没有了。

两次读取,得到的文件内容不一样。严重影响了新同事的学习进度。这就是不可重复读现象。

不可重复读。是指在数据库访问中,一个事务范围内的两个相同的查询却返回了不同数据。这是由于查询时,系统中其他事务修改的提交而引起的。比如事务T1读取某一数据,事务T2读取并修改了该数据,T1为了对读取值进行检验而再次读取该数据,便得到了不同的结果。

3.3 幻读

为了让新同事可以更好的学习代码。你们约定好,当她阅读某个类的代码的时候,通知你一下,然后你就不修改这个类的代码。避免出现了不可重复读的情况。

这样,你们之间的并发性就又降低了一些。不仅仅她阅读哪个类有了一些限制,你修改哪个类也有了要求了。

就这样相安无事了一段时间,她又来找你了。

在提升了隔离性之后,虽然你不会修改新同事正在阅读的类,她也不会阅读你正在修改的类。但是你可能会增加或者删除几个类。这时候和新同事之前读取到的类的总个数就有了变化。

也就是说,她之前读到的数据就不准确了。这就是幻读。

幻读。指同一个事务内多次查询返回的结果集不一样(比如增加了或者减少了行记录)。比如在同一个事务A内,第一次查询时候有n条记录,但是第二次同等条件下查询却又n+1条记,这就好像产生了幻觉。

幻读是不可重复读的一种特殊场景。

要想解决脏读、不可重复读、幻读等读现象,那么就需要提高事务的隔离级别。但与此同时,事务的隔离级别越高,并发能力也就越低。所以,还需要根据业务需求进行权衡。

4. 总结:

  • 事务的隔离性上,从低到高可能产生的读现象分别是:脏读、不可重复读、幻读
  • 脏读指读到了别的事务未提交的数据
  • 不可重复读指一次事务内的多次相同查询,读取到了不同的结果
  • 幻读是不可重复读的特殊场景。一次事务内的多次范围查询得到了不同的结果。

通过在写的时候加锁,可以解决脏读。
通过在读的时候加锁,可以解决不可重复读。
通过串行化,可以解决幻读。

事务隔离级别和脏读、不可重复读以及幻象读的对应关系如下

隔离级别脏读不可重复读幻象读
READ UNCOMMITTED允许允许允许
READ COMMITED不允许允许允许
REPEATABLE READ不允许不允许允许
SERIALIZABLE不允许不允许不允许
  • 7
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

止步前行

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值