Nothing lose,nothing gain.

关于脏读、不可重复读、幻读以及Mysql的隔离级别

关于这些问题,今天回顾的时候看了很多文章,发现之前有很多问题自己都是一知半解,所以重新写一篇文章,整理一下:

Mysql的隔离级别:

  • 读未提交(Read Uncommitted)
    在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别是最低的隔离级别,虽然拥有超高的并发处理能力及很低的系统开销,但很少用于实际应用。因为采用这种隔离级别只能防止第一类更新丢失问题,不能解决脏读,不可重复读及幻读问题

  • 读已提交(Read Committed)
    这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。这种隔离级别可以防止脏读问题,但会出现不可重复读及幻读问题

  • 可重复读(Repeatable Read)
    这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。这种隔离级别可以防止除幻读外的其他问题

  • 可串行化(Serializable)
    这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读、第二类更新丢失问题。在这个级别,可以解决上面提到的所有并发问题,但可能导致大量的超时现象和锁竞争,通常数据库不会用这个隔离级别,我们需要其他的机制来解决这些问题:乐观锁和悲观锁。

四种隔离级别会产生的问题:

《关于脏读、不可重复读、幻读以及Mysql的隔离级别》


脏读(Dirty Read)

在不同的事务下,当前事务可以读到另外事务未提交的数据:

A事务执行过程中,B事务读取了A事务的修改。但是由于某些原因,A事务可能没有完成提交,发生了RollBack操作,则B事务所读取的数据就会是不正确的。这个未提交数据就是脏读(Dirty Read)。脏读产生的流程如下:
《关于脏读、不可重复读、幻读以及Mysql的隔离级别》


不可重复读(Nonrepeatable Read)

B事务读取了两次数据,在这两次的读取过程中A事务修改了数据,B事务的这两次读取出来的数据不一样。B事务这种读取的结果,即为不可重复读(Nonrepeatable Read)。不可重复读的产生的流程如下:
《关于脏读、不可重复读、幻读以及Mysql的隔离级别》

这里拓展一下第二类丢失更新:

对于第二类丢失更新,也称为覆盖丢失,就是A和B一起执行一个数据,两个同时取到一个数据,然后B事物首先提交,但是A事物加下来又提交,这样就覆盖了B事物,称为第二类事物丢失,覆盖丢失。
《关于脏读、不可重复读、幻读以及Mysql的隔离级别》


幻读(Phantom Read)

B事务读取了两次数据,在这两次的读取过程中A事务添加了数据,B事务的这两次读取出来的集合不一样。幻读产生的流程如下:
《关于脏读、不可重复读、幻读以及Mysql的隔离级别》
这个流程看起来和不可重复读差不多,但幻读强调的是集合的增减,而不是单独一条数据的修改。


在这里还要再了解一下快照读和当前读:

快照读:

普通读(也称快照读,英文名:Consistent Read),就是单纯的 SELECT 语句,不包括下面这两类语句:
==SELECT … FOR UPDATE
SELECT … LOCK IN SHARE MODE==

在RR(可重复读)级别下, 普通读是通过MVVC(多版本并发控制)和undo log来实现的,普通读的执行方式是生成 ReadView,直接利用 MVCC (多版本并发控制)机制来进行读取,并不会对记录进行加锁。

对于 SERIALIZABLE 隔离级别来说,如果 autocommit 系统变量被设置为OFF,那普通读的语句会转变为锁定读,和在普通的 SELECT 语句后边加 LOCK IN SHARE MODE 达成的效果一样。

当前读:

当前读(也称锁定读,Locking Read),当前读,读取的是最新版本,并且需要先获取对应记录的锁,如以下这些 SQL 类型:
==select … lock in share mode 、
select … for update、
update 、delete 、insert==


InnoDB在RR级别下有没有幻读

最后还要记录一下的就是这个今天研究了许久的问题:InnoDB引擎在REPEATABLE READ级别下有没有幻读

看了很多文章,最后的结论为

在RR级别下InnoDB默认只能解决快照读的幻读问题,当前读的幻读还是通过加Next-Key Lock来解决的

InnoDB默认的隔离级别是RR(可重复读),可以解决脏读和不可重复读,只解决了快照读情况下的幻读问题,当前读情况下解决幻读问题得靠next-key锁。

这里补充下行锁的 3 种算法:

  • 行锁(Record Lock):锁直接加在索引记录上面。

  • 间隙锁(Gap Lock):是 Innodb 为了解决幻读问题时引入的锁机制,所以只有在 Read Repeatable 、Serializable 隔离级别才有。

  • Next-Key Lock :Record Lock + Gap Lock,锁定一个范围并且锁定记录本身 。

补充知识:

  • 什么是MVCC?
    多版本并发控制。InnoDB为每行记录添加了一个版本号(系统版本号),每当修改数据时,版本号加一。
    在读取事务开始时,系统会给事务一个当前版本号,事务会读取版本号<=当前版本号的数据,这时就算另一个事务插入一个数据,并立马提交,新插入这条数据的版本号会比读取事务的版本号高,因此读取事务读的数据还是不会变。

  • MySQL的innodb引擎是如何实现MVCC的

点赞

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注