阿里二面:了解 MySQL 事务底层原理吗(一)
你好,我是坤哥,今天是国庆最后一天,不知大家是否玩得尽兴,我基本在家带娃了,累得半死,顺带肝了一篇文,来自读者曾经在阿里的面试题,希望对大家有帮助,另外也欢迎大家加我微信「geekoftaste」,一起探讨技术问题,有疑问的我也许可以帮上忙^_^
MySQL 可以说是每个 Java 程序员必会的技能之一,作为 Java 的高级进阶必备技能点,MySQL 的调优和底层原理必然是需要知道的。
但是大家似乎形成了一种思维定势,那就是提到 MySQL 好像就一股脑的往 MySQL 的索引啊、优化啊、之类的上面去钻。本文我们抛开“热门”的话题,来和大家一起来聊一聊比较冷门但比较重要的技术点:MySQL 事务的底层原理
这事情还得从头说起
首先大家需要知道的是 MySQL 是支持事务并发执行的,这又回到了最原始的问题了,「并发安全性问题」。在数据库事务中并发问题是这样子的:A 事务来写某条记录的数据,B 事务也在写该条记录的数据。那如果啥也不做,势必会造成数据的错乱,MySQL 在设计之初就考虑到了这个问题。
那么 MySQL 到底是如何解决这样的问题的呢?其实是使用了 MVCC 多版本控制机制、事务隔离机制、锁机制等办法来解决事务并发问题。那说到这里不知道各位有没有想过这样一个问题:在数据库中如果并发事务不做控制和处理,会有什么样的危害呢?
带着这样的疑问,请继续往下看。
脏数据
什么是脏数据,它有哪些类型
脏数据的具体概念有以下四种,分别是:脏写、脏读、不可重复读、幻读。我们来看看这几个概念的意思
1、脏写
脏写是指一个事务修改且已经提交的数据被另外一个事务给回滚了
首先来分析一下概念:假设有两个事务 A、B。事务 A 先开启事务,并且修改了一条 id 为 1 的记录,将 name 改成 A(假设原来为 null),但是此时 事务 A 还没有提交。这个时候事务 B 开启了。事务 B 将 id 为 1 的记录中 name 改成了 B,并且将事务提交 了。但是这个时候事务 A 不想修改了,就像之前自己修改的数据回滚了。也就是说此时导致的结果就是 id 为 1 的这条记录的 name 还是为 null。
然后事务 B 去查询这条记录。结果蒙了。name 居然为 null。这就是脏写。事务 B 已经写入的记录被事务 A 给回滚了。
看不懂没有关系,我们先来看一张图
对着图再来看一下上面的分析过程。
那 MySQL 是如何来解决脏写这种问题的?没错,就是锁。MySQL 在开启一个事务的时候,他会将某条记录和事务做一个绑定。这个其实和 JVM 锁是类似的。因为此时事务 A 先开启了,并关联绑定了这条记录。所以事务 B 此时如果想操作同样的记录,只能等待。当事务 A 执行完成了,就会通知正在等在事务。然后下一个事务继续操作执行。
啥?说好的并发,这说到底不还是串行吗?这样数据库岂不是慢的要死。实际上这些操作都是在内存中执行的。具体一点是在 Buffer Pool 中执行的。所以速度是非常快的。
2、脏读
脏读是指一个事务读取到了另外一个事务没有提交的记录
其实脏读是最好理解的。我们还是假设有两个事务 A、B。事务 A 先开启了,将 id 为 1 的记录中的 name 改成了 A,但是还没有提交。此时事务 B 开启了。事务 B 查询到当前 name 的值为 A,然后就会按照 A 逻辑去执行处理。结果事务 A 回滚了事务,事务 B 再次查询的时候发现记录值不是 A。这就是脏读。
事务 B 读取到的 name 值是事务 A 修改但是没有提交的记录。
来张图来直观的理解下:
3、不可重复读
不可重复读是指前后读取到的某条记录的结果不一样
废话少说,直接进分析:假设有三个事务 A、B、C ,事务 A 先开启了,但是还没有执行任何的操作,事务 B 开启了,事务 B 将 id 为 1 的记录的 name 改为 B 并提交了事务,此时事务 A 开始活动了,查询到的这条记录的 name 值为 B,还是还未执行任何操作。此时事务 C 开启了,事务 C 将 id 为 1 的记录的 name 改为 C 并提交了事务。此时事务 A 又开始活动了,结果查询到的 id 为 1 的 name 值又变成了 C。这就是不可重复读
其实理解起来还是很简单的。看起来高大上名字,实际上就这么几句话就能描述结束了。下面还是来一张图来更直观认识下:
4、幻读
幻读是指前后读取到的记录的数量不一样
幻读和不可重复读有点类似,不可重复读强调的是数据的值不一样,重点是修改,而幻读强调的是记录的数量不一样,重点是新增或删除。就好像是看花眼产生重影一样。
先来分析一下幻读。还是假设有两个事务 A、B。事务 A 先开启了,并执行了这样的 SQL:select * from user,假设现在结果是 5 条,此时事务 B 开启了,并往 user 表中插入了一条记录,并提交了事务,此时事务 A 又执行了 select * from user结果发现是 6 条记录。懵逼了。还以为自己饿昏了眼花了。这就是所谓的幻读。
以上的四个问题是现代数据库典型的问题,这些问题会在不同的数据库的事务隔离级别下产生。所以下面要分析的就是事务的隔离级别。
事务的隔离级别
事务的隔离级别有以下四种
- Read Uncommitted:读取未提交【生产估计没人这么设置的】
意思就是一个事务能够读取到另一个事务未提交的修改 - Read Committed[简称 RC]:读取已提交
意思就是一个事务能读取到另一个事务已经提交了的修改 - Repeatable read[简称 RR]:可重复读
【MySQL 的默认隔离级别】,即事务之间只要是在进行中,彼此之间不会有任何的干扰 - serializable:串行化
这个就有点狠了,就好比 Java 中的 synchroinzed 关键字,所有的请求只能一个一个来还行,很显然效率最低,基本也不会使用这种隔离剂呗
文章转自公众号:码海