面试官:你有改过数据库的事务隔离级别吗?

Posted 赵小发

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面试官:你有改过数据库的事务隔离级别吗?相关的知识,希望对你有一定的参考价值。

这篇文章是我在杭州到汉口的火车上写的,可以说是冒着生命危险了。希望这篇文章能给我增加人品,最终平安到家。也祝福阅读这篇文章的同学们身体健康,远离病毒,过了安心年。

1. 前言

昨天发了一篇 synchronized 锁失效的文章(没看的同学可以先看下),群里有朋友说他也遇到过分布式锁失效的情况,他的解决方案是修改事务的隔离级别。我们来考虑一下,修改事务隔离级别能解决锁失效的问题吗?

2. 事务隔离级别

首先,我们来回顾一下基础的数据库知识。数据库事务的隔离级别有 4 个,由低到高依次为读未提交 (Read uncommitted) 、读已提交 (Read committed) 、可重复读 (Repeatable read) 、串行化 (Serializable) 。我们默认使用的 InnoDB 存储引擎默认的隔离级别是——可重复读 (Repeatable read) 。下面我依次介绍下各事务隔离级别以及对应存在的问题。

2.1 读未提交 (Read uncommitted)

假设有两个线程 1、2 同时开启了两个事务 A、B 。当线程1 执行业务逻辑,修改了数据库,但是此时事务 A 还没有提交,此时线程 2 在事务 B 中却能够看到线程 1 修改之后的新数据。这就是所谓的脏读。如果这个时候线程 1 之后的业务逻辑执行报错,最终事务没有提交,那其实最终修改数据库是失败的。但是线程 2 在中途却看到了线程 1 执行成功的数据,这不是很坑吗?

举个例子,快过年了,你告诉你女朋友你要给她银行转账 5201314 元,你转账的程序执行到一半,已经修改了你女朋友的账号余额,这个时候她看到了,非常开心,给你一个么么哒。然后你的程序事务提交失败了,最终没有转成功。然后你女朋友一刷新,发现余额又回去了。然后,还有然后吗?这个年,你还想过吗?

小结:读未提交比较坑,别用。

2.2 读已提交 (Read committed)

如果事务隔离级别是读已提交,不会出现上面的脏读问题,但是会造成下面的问题。

假设有两个线程 1、2 同时开启了两个事务 A、B 。事务 A 会修改某条数据库记录 X,而事务 B 会读取记录 X 。假设事务 B 开始读到的 X = 1 ,这个时候事务 A 修改了 X 的值为 2 ,此时事务 B 再次读 X 的记录时就变成 2 了。读已提交的意思就是在一个事务中多次读取某条数据,结果可能会不一样。

读已提交不会产生脏读,但是不支持重复读。

2.3 可重复读 (Repeatable read)

如果事务隔离级别是可重复读,不会出现上面的不支持重复读的问题,(这不是废话么,它的名字就是可重复读,)但是会造成下面的问题。

假设有两个线程 1、2 同时开启了两个事务 A、B 。事务 A 负责往某张数据表里新增数据,事务 B 负责读出该数据表里的所有数据。假设事务 B 需要读取两次,第一次刚开始读出来是 10 条数据,这个时候事务 A 新增了一条数据,提交事务成功,事务 B 第二次读取数据,发现读出来了 11 条数据。这时候出现的现象我们称之为幻读。如果事务 A 是修改或者删除了某条数据,事务 B 前后两次读取的都是一样的,可是新增就不一样了。

小结:不可重复读和幻读有点相似,他俩的区别在于不可重复读关心的是 update 和 delete 操作,幻读关心的是insert 操作。

2.4 串行化 (Serializable)

所有的读完全串行化,每次读都需要获得表级共享锁,读写相互都会阻塞。性能很低,一般很少使用,可以避免脏读、不可重复读、幻读。

2.5 隔离级别与脏读、不可重复读、幻读

关于每种隔离级别是否能够避免脏读、不可重复读、幻读这几种问题,《高性能mysql》这本书上已经总结好了,我直接截图了。(忽略最后一项加锁读)

3. 改隔离级别能解决锁失效吗?

我们回顾了事务的隔离级别,这个时候再来看看,修改事务的隔离级别,能解决使用 @Transactional 注解过程中导致的锁失效的问题吗?

群里的同学当时为了解决锁失效的问题,将事务的隔离级别改成了读已提交。这样做,锁就不失效了吗?

显然不是,锁失效的本质原因是当锁释放的时候,事务还没有提交,你把隔离级别改成读已提交没用啊。除非你自身的事务还没有提交,别的事务就能读取到你修改的数据。咦?这不就是读未提交吗?只要我修改了数据库,我不用提交事务别的事务也能读到我修改后的数据。那是不是修改事务隔离级别为读未提交就完美解决锁失效的问题了呢?

当然不是,2.1 已经明确指出了读未提交这种隔离级别存在的问题,如果为了解决锁失效就改成读未提交,这不是饮鸩止渴吗?

4. 隔离级别能改吗?

隔离级别尽量不要改,因为别人可能开发过程中基于默认的隔离级别可重复读在程序中做了相关的处理,你一改,不就给人挖了个大坑嘛。怎么,想上线的时候拿你祭天吗?

而且,不仅是隔离级别不要改,但凡是开发过程中这些基础配置相关的东西都不要随意改。

5. 写在最后

这篇文章写的比较入门,只是简单回顾了下事务隔离级别,也是临时加上的。因为群里的同学在看了昨天那篇文章后发现了自己系统的 bug ,所以今天特意写了一篇关于事务隔离级别的文章。当然,隔离级别的实现还依赖于数据库的各种锁机制。后面我写数据库相关的文章的时候再详细分析。

接下来一段时间,我会着重写多线程相关的文章,基本都是我在面试过程中会问的一些知识点。希望对大家有所帮助,毕竟,年后有很多同学都蠢蠢欲动了。感谢大家的支持!

关注我,每天早上八点半,叫醒你的不是闹钟,而是我的推送。


以上是关于面试官:你有改过数据库的事务隔离级别吗?的主要内容,如果未能解决你的问题,请参考以下文章

面试官:说一下MySQL事务隔离级别?

面试官:说一下MySQL事务隔离级别?

面经面试官问我:数据库中事务的隔离级别有哪些?各自有什么特点?然而。。。

拼多多面试官:说说数据库事务隔离级别

面试官:MySQL的可重复读级别能解决幻读问题吗?

互联网项目中mysql应该选什么事务隔离级别