InnoDB可重复读隔离级别是如何实现的

断断续续花了些时间,参考挺多文章,在自己的理解上总结出来以下内容,
以后忘了的时候可以翻出来看看。如果大家之前不太了解这一部分的东西,
可花点耐心把这篇看完,我想会对各位有一些帮助的,若无收获你来砍我。

mysql多事务并发的场景下会出现的问题:

1、脏读

事务A执行过程中的一次查询,读到了事务B修改但并未提交的数据,
后来B回滚,导致A读到了错误数据,这是脏读。
在这里插入图片描述

2、不可重复读

事务A两次相同查询的间隔中,事务B修改了这些记录的数据并提交,
导致A两次读到的记录数据不一致,这是不可重复读。
在这里插入图片描述

3、幻读

事务A两次相同查询的间隔中,事务B插入了符合条件的记录并提交,
导致A两次读到的记录条数不一致,这是幻读。
在这里插入图片描述

隔离级别

标准隔离级别总共有四种

1、读未提交(Read Uncommitted)

一般不会用于实际项目。

2、读已提交(Read Committed)

大部分数据库的默认隔离级别,如oracle、sql server等,
但是不包括mysql InnoDB。

3、可重复读(Repeatable Read)

mysql InnoDB默认的隔离级别。

4、可串行化(Serializable)

事务隔离的最高级别,会使数据库的并发性能大幅下降,现实中应用很少。

各隔离级别对应的问题:
在这里插入图片描述

常见锁

在分析今天的问题之前,我们先了解一些常见锁的基础概念。
在这里插入图片描述

乐观锁和悲观锁

它们不是真实存在的锁,只是一种机制或者说是理论。

共享锁(Shared Lock)也叫读锁或S锁

若事务A对数据对象a加上S锁,则事务A只能读a;
其他事务还可以再对a加S锁,却不能加X锁,直到A释放a上的S锁;
这样保证了其他事务可以读a,但在A释放a上的S锁之前不能对a做任何修改。

排它锁(Exclusive Lock)也叫写锁或X锁

若事务A对数据对象a加上X锁,则事务A可以读或写a,其他事务则不可以再对a加任何锁。

行锁(Record Lock)

锁住对应的行,使其它事务无法修改这些数据,但是无法限制insert操作。

间隙锁(Gap Lock)

锁住一个范围,使其它事务无法在该范围内插入数据。(这个我后面会再细说)

Next-Key Lock

Record Lock + Gap Lock。

MVCC(multiversion concurrency control)

多版本并发控制MVCC是以乐观锁为理论基础的一种可以用来增强并发性的强大技术。
在这里插入图片描述

事务版本号

事务版本号是一个从1开始的自增计数,是事务的唯一标识,
事务开启的时候分配,版本号的大小可以用来表示事务的串行化顺序,用于事务可见性的判断。

MVCC在InnoDB中的实现

在InnoDB中,会在每行数据后添加两个额外的隐藏的值,
这两个值一个记录这行数据何时被创建(创建它的事务的版本号),
另外一个记录这行数据何时被删除(删除它的事务的版本号)。

在RR可重复读隔离级别下的增删查改

delete:

记录对应行的删除版本号为当前事务版本号,delete也是假删除。

update:

1、删除原来记录,删除版本号同上;
2、新增一条记录,创建版本号为当前事务版本号。

insert:

同上第2步。

select:

查询创建版本号<=当前事务版本号and(删除版本号>当前事务版本号 or 删除版本号为null)的记录。

快照读(snapshot read)和当前读(locking read)

快照:

事务中第一个这样读读到的数据的一个readView,即该时间点数据的一个快照。

快照读:

在RR隔离级别下,同一事务中后边所有这样的读读到的都是第一个这样读的快照,
或是被当前事务刷新过的快照,这样做到了可重复读

当前读:

读取当前最新版本的数据。
(当前读的具体定义,大家可以去打听一下,这里只是顺便提一下。)

举个简单的例子:
假设事务A更新表中某一记录的a字段,而事务B也更新这条记录的b字段,
事务B先提交,如果事务A读取的是这条记录快照版本,那么它看不到事务B所提交的修改,
在此基础上更新的话就会覆盖B之前的修改,这样一来,B的修改就丢失了,而这样是不可接受的。

所以,在写的时候一定不是快照读,而是当前读。

InnoDB如何在可重复读级别解决了幻读的问题

可重复读

在这里插入图片描述
这段话的大致意思是,在默认的隔离级别中,普通的SELECT用的是快照读不加锁。
而对于当前读、UPDATE和DELETE,则需要加锁,至于加什么锁视情况而定:
如果对一个唯一索引使用了唯一的检索条件,那么只需锁定索引记录即可;
如果你没有使用唯一索引作为检索条件,或者用到了索引范围扫描,
那么将会使用间隙锁或者next-key锁以此来阻塞其它会话向这个范围内的间隙INSERT数据,避免幻读
如果你使用没有索引的字段作为检索条件,那么会给全表加上X锁!

间隙锁

下面是InnoDB的聚集索引结构图,其中非叶子节点里蓝色的区域即是索引之间的间隙。
在这里插入图片描述
假设一个索引包含以下几个值:10、11、13、20,那么表的索引间隙:
(负无穷, 10),
(10, 11),
(11, 13),
(13, 20),
(20, 正无穷)。

至于InnoDB为什么要采用间隙锁这样的方式去避免幻读,毕竟这样会不可避免地锁住一些非必要的区间,由于本人水平有限,不太明白,希望有懂得的人说一下。

参考文章:

狂乱的贵公子MySQL事务隔离级别的实现原理
amengInnodb中的事务隔离级别和锁的关系
风雨之间MySQL是如何解决不可重复读隔离级别中的幻读问题的
littlewhitMySql进阶-间隙锁(gap-key)
邓小闲答MySQL 是如何实现四大隔离级别的?

猜你喜欢

转载自blog.csdn.net/qq_35549286/article/details/108514103
今日推荐