读了
SnailMann大佬正确的理解MySQL的MVCC及实现原理收益颇丰,非常感谢!但对其中如何判断事务是否可见性还是不太理解,于是作了本文,在原博客基础上,举例画图论证、理解了ReadView的可见性判断。
引用
SnailMann大佬正确的理解MySQL的MVCC及实现原理的字段说明。隐式字段
每行记录除了我们自定义的字段外,还有数据库隐式定义的DB_TRX_ID,DB_ROLL_PTR,DB_ROW_ID等字段
DB_TRX_ID6byte,最近修改(修改/插入)事务ID:记录创建这条记录/最后一次修改该记录的事务ID
DB_ROLL_PTR7byte,回滚指针,指向这条记录的上一个版本(存储于rollbacksegment里)
DB_ROW_ID6byte,隐含的自增ID(隐藏主键),如果数据表没有主键,InnoDB会自动以DB_ROW_ID产生一个聚簇索引
ReadView的三个全局属性
trx_list(名称我随意取的):一个数值列表,用于维护ReadView生成时刻系统正活跃的事务ID列表
up_limit_id:是trx_list列表中事务ID最小的ID
low_limit_id:ReadView生成时刻系统尚未分配的下一个事务ID,也就是目前已出现过的事务ID的最大值+1为什么是low_limit?因为它也是系统此刻可分配的事务ID的最小值
可见性判断逻辑
DB_TRX_IDup_limit_id,当前行事务id比活跃的最小事务id还小时,说明了两件事,当前行事务对该记录的修改已经提交,因为当前事务id比活跃的最小事务id还小,不在活跃的事务之中,也就意味着该事务已经提交或回滚,这时因为已经成功修改,那么应该就是提交成功了。也就是在生成ReadView之前,事务已经提交,
接下来判断DB_TRX_ID=low_limit_id,修改该行的事务id大于了ReadView里系统待分配的下一个事务id,说明修改该行的事务是生成该ReadView之后出现的事务,因为ReadView系统待分配的下一个事务id被用了,才会出现比该事务id大的事务。这时,也应该是不可见的,一个事务怎么可以看到后面新来事务做的修改了。
判断DB_TRX_ID是否在活跃事务之中,trx_list.contains(DB_TRX_ID),如果在活跃事务之中,说明该修改是其他事务未提交的修改,应该是不可见的,如果可见就是脏读了,如果不在活跃事务之中,说明在生成ReadView之前,该事务的修改就已提交,与第一个判断逻辑类似,事务2是可以查到这条记录的。
针对上面三种情况,下面举例说明:
原记录:amount=
请问三次selectamount;快照读到的值分别是多少,为什么?
画一张图,把undo表里存的记录版本链及当前记录画出来。
①
1如图,当前行DB_TRX_ID(1)==up_limit_id(1),说明本次修改该记录的事务正在进行中,也就是事务1还未结束,事务2就应该对事务1这次修改不可见,可见就是脏读了。
2当前记录不可见,再根据回滚指针追踪到上个版本记录,如图undo日志内金额为的行,此时再通过ReadView进行可见性判断。
第一种情况:当前行DB_TRX_ID(3)up_limit_id(1),不确定;
第二种情况:DB_TRX_ID(3)low_limit_id(4),也不确定;
第三种情况:DB_TRX_ID(3)不在trx_list中,不是活跃的事务,说明事务3在事务2生成ReadView之前就已经提交,那么是可见的。
所以读取的金额为。
②
事务1提交事务,不过undo表与当前行数据无变化,对事务1的ReadView的数据也不会变化,因为RR模式下,ReadView只会在第一次快照读时生成,后面几次快照读不会生成新的ReadView,也不会改动之前ReadView的值。
当前行数据与ReadView都无变化,那么可见性判断也同①一致,读取到的金额为。
③
第一种情况:当前行DB_TRX_ID(4)up_limit_id(1),不确定;
第二种情况:DB_TRX_ID(4)low_limit_id(1),说明当前行是被生成ReadView之后出现的事务修改的,这种未来的数据肯定是不可见的。
再接着追溯,就与①中追溯的过程相差不大了,最终读取的金额也是为。
总结
这里举例论证了可见性判断的合理性,总结来说,可见性的三个判断约束了一件事,只有在本事务生成ReadView之前就已经提交的事务的修改才可以被看见,其他的无论是正在进行的事务的修改还是之后再提交的事务的修改都不可见。
来源: