因为①中的select语句生成了快照,之后的读操作(未加读锁)都是进行的快照读,即在当前事务结束前,所有的读操作的结果都是第一次快照读产生的快照版本。疑问又来了,为什么②步骤中的select语句读到的不是快照版本呢?因为update语句会更新当前事务的快照版本。具体参阅第五章节。
(2)repeatable-read利用当前读解决幻读重复(1)中的①②
③ session1进行插入
插入之前我们先看一下当前session1是否有id=4的数据
select * from tb_bank;
结果session1中没有该条记录,这时按照我们通常的业务逻辑,此时应该是能成功插入id=4的数据。
select * from tb_bank lock in share mode;//采用当前读
结果:发现当前结果中已经有小张的账户信息了,按照业务逻辑,我们就不在继续执行插入操作了。
这时我们发现用当前读避免了repeatable-read隔离级别下的幻读现象。
在此级别下我们就不再做serializable的避免幻读的sql演示了,毕竟是给整张表都加锁的。
五、当前读和快照读本想把当前读和快照读单开一片博客,但是为了把幻读总结明白,暂且在本章节先简单解释下快照读和当前读。后期再追加一篇MVCC,next-key的博客吧。。。
1、快照读:即一致非锁定读。
① InnoDB存储引擎下,查询语句默认执行快照读。
② RR隔离级别下一个事务中的第一次读操作会产生数据的快照。
③ update,insert,delete操作会更新快照。
四种事务隔离级别下的快照读区别:
① read-uncommitted和read-committed级别:每次读都会产生一个新的快照,每次读取的都是最新的,因此RC级别下select结果能看到其他事务对当前数据的修改,RU级别甚至能读取到其他未提交事务的数据。也因此这两个级别下数据是不可重复读的。
② repeatable-read级别:基于MVCC的并发控制,并发性能极高。第一次读会产生读数据快照,之后在当前事务中未发生快照更新的情况下,读操作都会和第一次读结果保持一致。快照产生于事务中,不同事务中的快照是完全隔离的。
③ serializable级别:从MVCC并发控制退化为基于锁的并发控制。不区别快照读与当前读,所有的读操作均为当前读,读加读锁 (S锁),写加写锁 (X锁)。Serializable隔离级别下,读写冲突,因此并发度急剧下降。(锁表,不建议使用)
2、当前读:即一致锁定读。如何产生当前读
① select ... lock in share mode
② select ... for update
③ update,insert,delete操作都是当前读。
读取之后,还需要保证当前记录不能被其他并发事务修改,需要对当前记录加锁。①中对读取记录加S锁 (共享锁),②③X锁 (排它锁)。
3、疑问总结① update,insert,delete操作为什么都是当前读?
简单来说,不执行当前读,数据的完整性约束就有可能遭到破坏。尤其在高并发的环境下。
分析update语句的执行步骤:update table set ... where ...;
InnoDB引擎首先进行where的查询,查询到的结果集从第一条开始执行当前读,然后执行update操作,然后当前读第二条数据,执行update操作......所以每次执行update都伴随着当前读。delete也是一样,毕竟要先查到该数据才能删除。insert有点不同,insert操作执行前需要执行唯一键的检查。补充一句:InnoDB引擎一定存在一个唯一键。
Linux公社的RSS地址:https://www.linuxidc.com/rssFeed.aspx