读《MySQL实战45讲》第八讲总结

事物消息如何实现MVCC(多版本并发控制)

  • 每个事务在启动时,都会向InnoDB申请一个id,称为transaction id,这个id是唯一的,并且是按申请顺序严格递增的。
  • 数据表中每一行是多版本的,也就是说对于同一行,每次事务更新都会生成一个新的版本,这个版本就记为row trx_id,这个row trx_id的值其实就是最新一次更新的事务的transaction id。而且旧版本是会保留的。
  • 事物在启动时,会找到所有已经提交的事务的最大transaction id作为up_limit_id,具体实现上,InnoDB同时还会为事务构造一个数组,用来保存所有在事务启动之前已经启动了但未提交的的事务的transaction id。
  • 事务在更新一条语句的时候,比如对ID = 1 的行,将其某一列的值c由3改为4,它会先将c = 3的时候该行的row trx_id保存到undo log(也就是实现了旧版本是会保留的这一概念)。然后在数据页将c由3改为4,最后将自己的row trx_id记在该行的行头。
  • 如果事务是要查询的话,它需要先将自己的up_limit_id与要查询的那一行的row trx_id作比较,如果up_limit_id < row trx_id,则需要到unlog里去查找up_limit_id >= row trx_id时的版本记录。
  • 重点记住:可重复读的隔离条件下,事务启动以前的提交是可见的,事务启动后的提交是不可见的。
  • 例子:事务A的transaction id是100,在A启动之前,transaction id为90的事务提交了一次更新,row trx_id变为90,即已经提交了的事务的最大transaction id是90,就是说up_limit_id值为90。此外,系统里还有一个活跃(启动但未提交)的事务X,transaction id为99,此时A记录的数组就是[99,100]。在A启动后,如果有事务B(transaction id = 88)提交了一次更新,row trx_id变为88,然后事务A执行了一次查询,此时A查询到的值是row trx_id为90的版本对应的值。因为虽然88 < 90,也即up_limit_id > row trx_id,但是B不在数组[99,100]中,也就是B是在A启动后提交的,对于A是不可见的。
  • 重点记住:事务更新时基于当前读的,就是更新的时候读取的数据一定是最新的数据,否则会导致之前的更新丢失。
  • 例子:事务C记录的数组为[99,100,101],事务D记录的数组为[99,100,101,102],两个事务的p_limit_id值均为90,事务D先执行一次更新并且立马提交,此时row trx_id = 102,那么之后事务C要更新时,读取到的值是row trx_id = 102的版本的值。如果事物D执行更新后没有马上提交哦啊,事物C也想更新,此时事务C无法执行更新,因为基于两阶段锁协议,事务D未提交,也就是写锁未释放,而事务C是当前读,而且也需要写锁,所以事务C需要等到事务D提交释放写锁后才能更新。
  • 重点记住:可重复读隔离与读提交隔离的不同在于视图创建时间不同,可重复读隔离下的事务的视图创建于事务启动的时候,而读提交隔离下的事务的视图创建于语句执行之前,而且每一个新语句执行前会新创建一个视图

参考

《MySQL实战45讲》第八讲:事务到底是隔离的还是不隔离的?

猜你喜欢

转载自blog.csdn.net/qq_34770694/article/details/84762070