可重复读(repeatable read)定义:一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据时是一致的。
不过理论上 是会出现幻读的,简单的来说幻读指的是当用户读取某一范围的数据行时,另外一个事务又在该范围插入了新行,当用户再次读取该范围的数据时会发现 出现新的幻影行。
注意可重复读隔离级别下,普通的查询是快照读,是不会看到别的事物插入的数据的。因此,当前在”当前读“下才会出现 幻读。
在mvcc并发控制中,读操作可以分为两类:快照读 和当前读。
快照读:
快照读 :是 在读取的数据 时 不是读取最新版本的数据。而是基于历史版本读取的一个快照信息(mysql 读取 undo log 历史版本),快照读可以使用普通的select 读取时不用在对表数据 及逆行加锁,从而解决了因为数据库表的加锁而导致的两个问题
1:解决了因加锁导致的修改数据时无法对胡数据读取问题
2:解决了因加锁导致读取数据时无法对进行修改的问题。
当前读:读取的数据最新的数据,当前读和快照读不同,因为读取最新的数据而且保证事务的隔离性,所以当前读 是需要对数据进行加锁的。
下面举个列子:
表结构
id | key | value |
---|---|---|
0 | 0 | 0 |
1 | 1 | 1 |
假设 select * from where value=1 for update,只在这一行加锁(注意这只是假设),其它行不加锁,那么就会出现如下场景:
Session A的三次查询Q1-Q3都是select * from where value=1 for update,查询的value=1的所有row。
-
T1:Q1只返回一行(1,1,1);
-
T2:session B更新id=0的value为1,此时表t中value=1的数据有两行
-
T3:Q2返回两行(0,0,1),(1,1,1)
-
T4:session C插入一行(6,6,1),此时表t中value=1的数据有三行
-
T5:Q3返回三行(0,0,1),(1,1,1),(6,6,1)
-
T6:session A事物commit。
其中Q3读到value=1这一样的现象,就称之为幻读,幻读指的是一个事务在前后两次查询同一个范围的时候,后一次查询看到了前一次查询没有看到的行。
产生幻读的原因是:行锁只能锁住行,但是新插入记录的这个动作,要更新的是记录之间的'间隙'。因此,Innodb 引擎 为了解决[ 可重读]隔离级别下 [当前读]而造成的幻读问题,就引出了 next_key锁,就是记录锁和 间隙锁的组合。
1:RecordLock锁:锁定单个行记录的锁。
2:CapLock锁:间隙锁,锁定索引记录间隙,确保索引记录的间隙不变(范围锁,RR隔离级别支持)
3:Next-key Lock 锁: 记录时锁和间隙锁组合。锁住数据的同时,并且锁住 数据前后范围。(记录锁+范围锁,RR隔离级别支持)