首先,数据库中每条记录都有两个隐藏列一个是trx_id(事务id),一个是roll_pointer(指向undo日志的指针)
可重复读的思想是在启动事务时创建一个ReadView,ReadView可以视为一个数据快照。在整个事务的执行过程中,都会使用这个ReadView。以保证在整个事务的执行过程中,数据是一致的。用以解决脏读和不可重复读且大概率不出现幻读。
ReadView的结构有
Creator_trx_id: 创建该ReadView的事务id
m_ids:活跃事务的id列表(指操作了数据记录,但是没有提交的事务)
min_trx_id: 活跃事务id列表中最小的事务id
max_trx_id:全局事务id最大值的下一个id
当事务A启动时,就会创建ReadView,事务A去访问数据记录时,首先查看数据记录的事务id,是否在m_ids中,一般来讲已经存在的数据记录的事务id应该比当前事务Id小,如果该数据记录的事务id小于min_id,那么表示该记录是之前的事务已经提交的,所以事务A对该记录可见,可以访问。
如果此时启动了事务B,那么他也会创建ReadView,与事务A同理,它也可以访问该数据记录,
如果事务A此时修改了数据,但是没有提交,事务B去读取该数据记录时,该数据对事务B是不可见的,事务B读取的仍然是原数据。(解决脏读)
当事务A提交事务后,会把之前版本的记录写进Undo日志,然后在roll_pointer列设置一个指针指向该记录。
事务B去读取该记录时由于事务隔离级别为可重复读,他会去读取事务A的roll_pointer指针指向的undo日志的第一条记录,也就是该记录更新前的那一个版本。所以数据B读取到的仍然时原数据。(解决不可重复读)
由此实现了可重复读