# 事务B,Transaction ID 200begin; //开个事务,占坑先
这时候,account表中,id =1记录的undo日志链如下:
# 事务C,Transaction ID 300begin;//查询1:select * from account WHERE id = 1;
查询1执行过程分析:
事务C在执行SELECT语句时,会先生成一个ReadView。因为事务A(100)、B(200)在活跃,所以ReadView的m_ids列表内容就是[100, 200]。
由上图undo日志链可得,最新版本的balance为1000,它的事务ID为100,在活跃事务列表里,所以当前事务(事务C)不可见。
我们继续找下一个版本,balance为100这行记录,事务Id为50,小于活跃事务ID列表最小记录100,所以这个版本可见,因此,查询1的结果,就是返回balance=100这个记录~~
接着,我们把事务A提交一下:
# 事务A,Transaction ID 100begin;UPDATE account SET balance = 1000 WHERE id = 1;commit;
在事务B中,执行更新操作,把id=1的记录balance修改为2000,更新完后,undo 日志链如下:
# 事务B,Transaction ID 200begin; //开个事务,占坑先UPDATE account SET balance = 2000 WHERE id = 1;
回到事务C,执行查询2
# 事务C,Transaction ID 300begin;//查询1:select * from account WHERE id = 1;//查询2:select * from account WHERE id = 1;
查询2:执行分析:
在RR 级别下,执行查询2的时候,因为前面ReadView已经生成过了,所以直接服用之前的ReadView,活跃事务列表为[100,200].
由上图undo日志链可得,最新版本的balance为2000,它的事务ID为200,在活跃事务列表里,所以当前事务(事务C)不可见。
我们继续找下一个版本,balance为1000这行记录,事务Id为100,也在活跃事务列表里,所以当前事务(事务C)不可见。
继续找下一个版本,balance为100这行记录,事务Id为50,小于活跃事务ID列表最小记录100,所以这个版本可见,因此,查询2的结果,也是返回balance=100这个记录~~
锁相关概念补充(附):
共享锁与排他锁
InnoDB 实现了标准的行级锁,包括两种:共享锁(简称 s 锁)、排它锁(简称 x 锁)。
共享锁(S锁):允许持锁事务读取一行。
排他锁(X锁):允许持锁事务更新或者删除一行。
如果事务 T1 持有行 r 的 s 锁,那么另一个事务 T2 请求 r 的锁时,会做如下处理:
T2 请求 s 锁立即被允许,结果 T1 T2 都持有 r 行的 s 锁
T2 请求 x 锁不能被立即允许
如果 T1 持有 r 的 x 锁,那么 T2 请求 r 的 x、s 锁都不能被立即允许,T2 必须等待T1释放 x 锁才可以,因为X锁与任何的锁都不兼容。
记录锁(Record Locks)
记录锁是最简单的行锁,仅仅锁住一行。如: SELECT c1 FROM t WHERE c1=10FOR UPDATE
记录锁永远都是加在索引上的,即使一个表没有索引,InnoDB也会隐式的创建一个索引,并使用这个索引实施记录锁。
会阻塞其他事务对其插入、更新、删除
记录锁的事务数据(关键词:lock_mode X locks rec butnotgap),记录如下:
RECORD LOCKS space id 58 page no3 n bits 72 index `PRIMARY` of table `test`.`t` trx id 10078 lock_mode X locks rec but not gapcordlock, heap no2 PHYSICAL RECORD: n_fields 3; compact format; info bits 00: len 4; hex 8000000a; asc ;;1: len 6; hex 00000000274f; asc 'O;; 2: len 7; hex b60000019d0110; asc ;;
间隙锁(Gap Locks)
间隙锁是一种加在两个索引之间的锁,或者加在第一个索引之前,或最后一个索引之后的间隙。
使用间隙锁锁住的是一个区间,而不仅仅是这个区间中的每一条数据。
间隙锁只阻止其他事务插入到间隙中,他们不阻止其他事务在同一个间隙上获得间隙锁,所以 gap x lock 和 gap s lock 有相同的作用。
Next-Key Locks
Next-key锁是记录锁和间隙锁的组合,它指的是加在某条记录以及这条记录前面间隙上的锁。
RC级别存在幻读分析
因为RC是存在幻读问题的,所以我们先切到RC隔离级别,分析一波~
假设account表有4条数据。
开启事务A,执行当前读,查询id>2的所有记录。
再开启事务B,插入id=5的一条数据。
事务B插入数据成功后,再修改id=3的记录
回到事务A,再次执行id>2的当前读查询
事务B可以插入id=5的数据,却更新不了id=3的数据,陷入阻塞。证明事务A在执行当前读的时候在id =3和id=4这两条记录上加了锁,但是并没有对 id > 2 这个范围加锁~
事务B陷入阻塞后,切回事务A执行当前读操作时,死锁出现。因为事务B在 insert 的时候,会在新记录(id=5)上加锁,所以事务A再次执行当前读,想获取id> 2 的记录,就需要在 id=3,4,5 这3条记录上加锁,但是 id = 5这条记录已经被事务B 锁住了,于是事务A被事务B阻塞,同时事务B还在等待 事务A释放 id = 3上的锁,最终产生了死锁。
因此,我们可以发现,RC隔离级别下,加锁的select, update, delete等语句,使用的是记录锁,其他事务的插入依然可以执行,因此会存在幻读~
RR 级别解决幻读分析
因为RR是解决幻读问题的,怎么解决的呢,分析一波吧~
假设account表有4条数据,RR级别。
开启事务A,执行当前读,查询id>2的所有记录。
再开启事务B,插入id=5的一条数据。
可以发现,事务B执行插入操作时,阻塞了~因为事务A在执行select ... lock in share mode的时候,不仅在 id = 3,4 这2条记录上加了锁,而且在id > 2 这个范围上也加了间隙锁。
因此,我们可以发现,RR隔离级别下,加锁的select, update, delete等语句,会使用间隙锁+ 临键锁,锁住索引记录之间的范围,避免范围间插入记录,以避免产生幻影行记录。
本文地址:百科问答频道 https://www.neebe.cn/wenda/903503_3.html,易企推百科一个免费的知识分享平台,本站部分文章来网络分享,本着互联网分享的精神,如有涉及到您的权益,请联系我们删除,谢谢!