2

前言

最近在准备春招的过程中顺手复习了一下MySQL的事务隔离级别,现在整理出来和各位同学一起学习,若有不对的地方请指教。

在这之前先看看相关的面试题:

  • MySQL有哪些事务隔离级别?
  • Read Commit(读已提交)级别是如何解决脏读的?
  • Repeatable Read(可重复读)级别是如何解决不可重复读的?
  • MySQL是如何解决幻读的?MVCC可以解决幻读问题吗?

相关概念

首先先介绍一下脏读、不可重复读和幻读:

脏读:指一个事务可以看到另一个事务未提交的数据。

比如说事务A修改了一个值但是还未提交,这时事务B可以看到A修改的值,这就是脏读。

不可重复读:一个事务执行两次同样的查询语句,前后得出的数据却不一致。

比如说事务A执行了select语句,事务B修改了某个值,事务A再次执行select语句时发现结果和上次不一致,因此叫做不可重复读。

幻读:在同一个事务中,同一个查询多次返回的记录行数不一致(这里的结果特指查询到的记录行数,幻读可以看做不可重复读的一种特殊情况)。

比如说事务A执行了select语句,事务B插入数据,事务A再次执行select语句时发现多了几条记录,好像出现了幻觉一样,因此叫做幻读。

MySQL有哪些事务隔离级别?

级别从低到高分别是:未提交读已提交读可重复读串行化

下图是各个隔离级别以及可能会出现的问题:

Read Commit(读已提交)级别是如何解决脏读的?

先说结论:通过改变锁的释放时机来解决脏读问题

首先先了解一下为什么会出现脏读?原因就是在未提交读这个级别下,当事务A修改了数据之后就立马释放了锁,因此事务B可以读取到这个未提交的数据。

已提交读级别下写操作加的锁会到事务提交后释放,所以事务B不会读到事务A未提交的数据,通过改变锁的释放时机解决了脏读的问题。

Repeatable Read(可重复读)级别是如何解决不可重复读的?

结论:可重复读级别就是通过MVCC机制来解决不可重复读问题的

MVCC机制(多版本并发控制)就我个人理解来说其实就是给每行数据都添加了几个隐藏字段,用来表示数据的版本号,即一个数据在mysql中会有多个不同的版本,想深入了解MVCC请参考:MVCC机制

MVCC就是给每一行都都有个事务版本号,假设一条链表第一个节点是最新的数据,越后数据越旧,当有一个快照读操作过来后,会遍历链表,找到第一个当前事务可见的数据。

有了MVCC之后我们可以把SQL操作分为两类:

  • 快照读

读取当前事务可见的数据,默认的select操作就是快照读,读的是历史版本的数据。

  • 当前读

读取最新的数据,除了默认select操作外的select..for updateupdateinsertdelete等操作都是当前读,读取的都是最新的数据。

现在我们有了MVCC,当事务A执行一个普通的select操作(快照读),MySQL会把这次读取的数据保存起来,在这期间不管事务B执行update或是insert操作,事务A再次执行select操作读取到的数据是不会变的,因此通过可重复读级别通过MVCC解决了不可重复读问题,顺便解决了部分的幻读问题,没错MVCC并没有解决所有的幻读问题,只是解决了一部分,那是如何彻底解决幻读问题的呢?请接着往后看。

MySQL是如何解决幻读的?MVCC可以解决幻读问题吗?

先说结论:MVCC并不能解决所有的幻读问题,MySQL是通过MVCC + Next Key Lock来解决幻读问题的。

为什么MVCC不能解决幻读?

因为当事务执行的是加锁的select操作(当前读),每次执行都是读取最新的数据,事务A是可以看到事务B修改的数据的,那么还是会出现幻读的问题,因此MySQL引入了一种叫做Next Key Lock的算法,简单来说就是对一个范围加锁,这样子当事务A执行select..for update时会对数据加锁,这时事务B是无法执行updateinsert等操作的,因此MySQL是通过MVCC + Next Key Lock来解决幻读问题的,有关Next Key Lock请参考Innodb锁机制:Next-Key Lock 浅谈

总结

有关MySQL隔离级别就讲到这里啦,这些都是本人的一些心得,如果有什么不对的地方请各位大佬们多多指教!


超大只乌龟
882 声望1.4k 粉丝

区区码农