1、定义
不可重复读: 事务1读取一行,事务2然后修改或删除该行数据并且提交事务,事务1再次读取结果不一样;
幻读:事务1按条件读取查询数据,事务2按照同样的条件新增一条或多条数据并且提交事务,事务1再次读取结果不一致
版本链:对该条记录每次更新后,都会将旧值放到一条undo
日志中,随着更新次数的增加,最新的版本加上之前的所有旧版本就会被roll_pointer
属性连接成一个链表,这个链表就是版本链
,版本链的头节点就是当前记录最新的值
每次使用
update
对记录进行改动时,都会记录一条undo
日志,每条undo
日志也都有一个roll_pointer
属性(insert
操作对应的undo
日志没有该属性,因为该记录并没有最早的版本),可以将这些undo
日志连接起来形成一个链表
readview的四个属性
1、creator_trx_id:当前事务id
2、m_ids:当前系统中所有未提交的事务id的集合
3、min_trx_id:m_ids中最小的事务id
4、max_trx_id: m_ids中最大的事务id在加1,也就是下一个要生成的事务id
2、原理:
文字描述:
1、版本的trx_id==READVIEW中的creator_trx_id,表示当前读事务正在读取被自己修改过的记录,该版本可以被当前事务访问;
2、版本trx_id < min_trx_id,表明生成这个版本的事务trx_id 在当前事务creator_trx_id生成READVIEW前已经提交了, 所以该版本可以被当前事务访问;【说明我在开启事务的时候,这条记录肯定是还没有的,是在之后这条记录才被创建的,不应该被当前事务看见,这时候我们就可以通过 回滚指针 + Undo Log 去找一下该记录的历史版本,返回给当前事务】
3、版本的trx_id > max_trx_id,表明生成该版本的事务在当前事务生成READVIEW后才开启的,
该版本不可被当前事务访问;
4、版本的trx_id在READVIEW的min_trx_id和max_trx_id之间,需要分两种情况
1) trx_id 在 m_ids 数组中,那么当前事务不能读取到。为什么呢?
row_trx_id 在 m_ids 数组中表示的是和当前事务在同一时刻开启的事务。(保证可重复读)
2) trx_id 在不在m_ids 数组中,那么当前事务可以读到?【只会发生在读已提交】
说明该事务在生成redview的时候已经被提交 所以当前事务可以读取该数据
读已提交rc:每次读取都会新生成一个readview快照;
可重复度rr:是以一个事务为单位,只在第一次读取(select)数据时生成ReadView;
(快照读是基于MVCC+undo log来实现的,所以快照读只存在于RC和RR隔离级别下;未提交读和串形化隔离级别下都是当前读)
读已提交,出现不可重复读是因为每次查询都是一个新的readview(产生新的trxId),也就是说查询之前,可以读取到已经提交的事物,(王五-->赵六),可重复读一直在一个rearview内,前后都是一个事物(ID=1),当发现版本4不在m_ids[1,2,3]内,那么会根据roll_pointer指针去undo_log的版本链需找前一个,也就是trx_id=3,得到name=王五,也就是可重复度;
那RR级别为什么不能解决幻读呢?
幻读是由于第二次读物的数据 新增了数据,而insert的时候,属于第一次进入undoLog,没有历史版本,他的roll_pointer=null,对应数据values ('王五',18),通过name=王五,查找到age=18这个数据的时候,没有办法找到之前到版本,于是返回了age=18,最后结果就是两条:age=20和18;
mysql怎么协助rr解决幻读?
通过间隙锁