InnoDB如何用锁实现事务的隔离

要想搞清楚innoDB如何实现事务隔离级别,首先需要了解什么是事务的隔离级别。

隔离级别

SQL标准定义了4种事务隔离级别:

  1. READ UNCOMMITED
  2. READ COMMITED
  3. REPEATABLE READ
  4. SERIALIZABLE

    READ UNCOMMITED(未提交读)

    未提交读是指两个事务A,B。事务A在读取数据库一行记录的同时事务B在修改该记录,事务B对该记录修改后还未提交的时候,事务A就可读到修改后的记录。这里写图片描述
    上图描述了READ UNCOMMITED的场景,这种情况称之为脏读,READ UNCOMMITED 会导致脏读的问题。

READ COMMITED(提交读)

脏读会导致一个问题,要是事务B提交失败,导致事务回滚,那么实际上i=1,但是事务A中i=2,这样就导致了数据不一致。READ COMMITED中解决了脏读的问题,在事务B提交之前,事务A读的还是未修改的数据,只有事务B成功提交,事务A才能够获取被事务B修改的数据。这里写图片描述
上图描述了READ COMMITED的过车,我们可以看到该过程解决了脏读的问题,即只有事务B提交过后,事务A才能读取修改的值。但是图中也反映出新的问题,事务B提交完后,又来了一个事务对i做修改并且提交。这时,事务A还没结束,继续读i的值,又得到一个新的值。也就是说在事务A的生命周期中对数据库中的记录i读取了3次,每次获取的数据都不同:1,2,3。因此在READ COMMITED隔离级别下就暴露出了不可重复读的问题。
####REPEATABLE READ(可重复读)

为了解决不可重复读的问题,有了REPEATABLE READ这个隔离级别,它能保证事务周期中对同一记录的重复读取,获取的值是不改变的。
这里写图片描述

从上图可知,在事务A的生命周期中,无论i的值被怎么改变,事务A都是读取i在事务开始时的快照。

SERIALIZABLE

REPEATABLE READ 下虽然解决了重复读的问题,但是还有其他的问题没解决,看下图:
这里写图片描述
虽然REPEATABLE READ 已经解决了更新记录的重复读问题,但是,对于插入、删除数据,还是无法保持数据的一致。这就是幻读的问题,SERIALIZABLE隔离级别下能解决该问题。
这里写图片描述
在范围查询的时候,数据库给范围加锁,事务周期内其他事务不得在此范围内插入删除,这样就解决了幻读的问题。

InnoDB对各种隔离级别的实现

清楚了隔离级别的概念之后,让我们看看MySQL的InnoDB是如何通过锁实现对这些隔离级别的支持的。

READ UNCOMMITED的实现

这种隔离级别是最低的,也是约束最少的,因此不加任何锁的情况下就是READ UNCOMMITED的实现

READ COMMITED的实现

该隔离级别解决了脏读的问题,InnoDB使用一致性非锁定读的技术来实现的。那么什么是一致性非锁定读呢?

扫描二维码关注公众号,回复: 1733166 查看本文章
一致性非锁定读

一致性的非锁定读是指InnoDB存储引擎通过多版本控制(MVCC)的方式来读取当前执行行时间数据库中的数据。如果读取的行正在执行DELETE或UPDATE操作,这时读取操作不会因此去等待行上锁的释放。相反地,InnoDB存储引擎会去读取行的一个快照数据。这里写图片描述
在READ COMMITED隔离级别下,如果要读取的行被上锁(事务B未提交),则读取最新的快照,因此能解决脏读的问题。

REPEATABLE READ的实现

如前文所述,一般的情况下REPEATABLE READ解决的是不可重复读的问题,SERIALIZABLE解决的是幻读的问题。但是InnoDB有点厉害了,它在REPEATABLE READ下就把不可重复读和幻读这两个问题都解决了,也就是说它把SERIALIZABLE活也干了。那么在REPEATABLE READ下是如何实现对这两个问题的解决的呢?
对于不可重复读的问题,解决的方法还是一致性非锁定读。不过在这里它读取快照的策略与READ COMMITED下不一样。在READ COMMITED下是读取最新的快照,而REPEATABLE READ下是读取事务最开始的快照,无论这条记录被其他事务怎么改,在我的事务内我都是读取最开始的那个快照,以此来保证数据的可重复读。
对于幻读的问题,InnoDB使用了一致性锁定读中的Next-Key Lock算法解决。我们首先介绍一下什么是Next-Key Lock。

Next-Key Lock

当InnoDB采用一致性锁定读的时候,就是给记录加锁(X锁即排他锁,S锁即共享锁)。InnoDB存储引擎有3中锁的算法:

  • Record Lock:单个行记录上的锁
  • Gap Lock:间隙锁,锁定一个范围,但不包括记录本身
  • Next-Key Lock:Gap Lock+Record Lock,锁定一个范围,并且锁定记录本身。

    在Next-Key Lock锁算法下,查询i>0的时候,(0, + )范围会被上锁,这时候事务B要在该范围内插入记录就需要阻塞,就这样幻读问题就解决了。

猜你喜欢

转载自blog.csdn.net/qqqq0199181/article/details/80782822