
事务A读取表中的name,事务B将表中的张三修改称为了张老三,假设这时候事务A读取到了name为张老三,但是到最后事务B又因为其他原因回滚了数据,数据库表中的name为张三,那这就出现了问题,事务A读到的数据和数据库表中的不一致.而且这个数据变化到底是因为原因造成的事务A是不知道的

事务A先查询了数据库中的人员名字,之后就去处理其他的数据,之后有重复了最开始查询的内容,那么事务A预期的应该是两次查询的结果应该是一致的,因为事务A在处理其他数据的时候,没有对查询的数据做一个修改
但是问题是在事务A处理其他数据的时候,事务B对事务A查询的数据做了更新且提交了,那么事务A和事务B在并发的情况下,两次查询的结果就不一致了
但是实际的理论上我们事务A没有对数据进行更新,那么在事务A在没有提交之前,无论查询多少次数据都应该是一致的才对,这个理论情况叫做可重复的
上面并发出现的这个情况叫做不可重复读
同一个事务前后两次读取的数据不一致,就导致我们的程序不可预期不可控,这也就是不可重复读带来的危害

在这个例子当中,事务A先查询了数据库表发现有一条数据,他在删除了这个数据之后,事务B向数据库表当中插入了一条数据,事务A此时还没有提交,当事务A再次查询数据库表的收,预期结果应该是空数据,但是凭空多出来这个李四是什么鬼,这个就是这两个事务在并发情况下发生的幻读问题

其中MySQL默认的事务隔离级别是第三个:可重复读,比较特殊的要记住就是,常常说可重复读事务隔离级别能避免脏读和不可重复读,但是不能避免幻读,但是实际上如果我们的数据库使用的是InnoDB存储引擎是不存在幻读的.

在MySQL的InnoDB存储引擎下 RC和RR 事务隔离级别都是基于这个MVCC(多版本并发控制)进行并发事务的控制的
然后这个MVCC是基于这个"数据版本"对并发事务进行访问的

有四个事务ABCD其中前三个事务执行的操作都是对表中name字段进行修改
事务D执行的操作是进行数据的查询,但是仔细看事务D查询的位置还特殊的不行,第一次查询是在事务A提交之后事务B提交之前,第二次查询数据是在事务B提交之后在事务C提交之前
RR隔离级别是可重复读,那么就意味着在整个事务中读取到的数据是一致的那么显而易见第一次查询和第二次查询得到的数据是一致的,即name=张三
RC隔离级别下
由于RC是读已提交,只要别的事务提交数据,我就进行读取,那么实际上最后RC隔离级别下两次读到的数据分别是: 第一次:张三 第二次:张小三
显而易见RC隔离级别下出现的问题就是不可重复读

UNDO_LOG版本链不是立即删除的,MySQL确保版本数据链不再被引用之后在进行删除,所以除了一些不可控的因素之外,一般数据都是比较完整的
insert into ...
update ...
delete ...
select ... for update
# 对当前增加了写锁
select ...lock in share mode

在每一次执行快照读的时候生成全新的ReadView

版本链数据访问规则:
- 判断当前事务id等于creator_trx_id(4)吗?成立说明数据就是自己这个事务更改的,可以访问.
- 判断trx_id < min_trx_id(2) ? 成立则说明数据已经提交了,可以访问.
- 判断trx_id > max_trx_id(5) ? 成立则说明该事务是在readview生成以后才开启的,不允许访问.
- 判断min_trx_id(2) <= trx_id <= max_trx_id(5),成立在m_ids数据中进行对比,不存在则代表数据是已经提交的,可以进行访问.
让undo_log版本链中的版本从小到校一次带入到这个规则当中进行判断找到第一个可以访问的数据.
这个快照读带入可以在trx_id = 1这个版本的时候,满足trx_id < min_trx_id(2) 说明数据已经提交可以访问所以第一次查询语句查询出来的数据就是张三.
这个快照读可以在trx_id = 2的时候满足规则中的第二条 trx_id < min_trx_id(3)说明数据已经提交了,读取的数据为张小三 !!!出现了不可重复读的现象

连续多次快照读的时候,ReadView会产生复用,没有幻读问题
但是需要注意的是:当两次快照读之间存在当前读,ReadView会重新生成,导致产生幻读