《MySQL》事务隔离性性的底层原理——MVCC

文章目录

  • 预备知识
    • 1. 表中的四个隐藏字段
    • 2. undo日志
    • 3. ReadView
  • RC和RR的区别

前文有聊到事务的基本概念,基本操作,以及最后抛出来个隔离级别,本文继续讲解事务隔离级别中读提交(RC)和可重复读(RR)的实现原理。

补充知识

  • 每个事务都有一个唯一的事务ID,并且越早创建的事务ID越小
  • 事务有着自己的生命周期

预备知识

1. 表中的四个隐藏字段

  • DB_TRX_ID:6 byte,事务ID
  • DB_ROLL_ID:7 byte,回滚指针,指向这条记录的上一个版本。
  • DB_ROW_ID:6 byte,隐藏主键
  • 删除flag的隐藏字段:删除一条记录时,是表修改该记录的flag,而不是真正意义上的消失。

解释一下什么是隐藏主键,每张表都会有一个默认的隐藏主键,如果该表没有设置主键,InnoDB会自动以 DB_ROW_ID 产生一个聚簇索引,在展示数据时,就会按照默认主键遍历每个B+树中的数据叶子节点。

假设有张表stu,且只有一条数据,id为主键

id name gender DB_TRX_ID(修改该事务的id) DB_ROLL_ID DB_ROW_ID flag
1 张三 null null null false

DB_ROLL_ID 为null,为什么?是因为一旦事务提交(commit)了,就没有历史版本了,所以为空
DB_ROW_ID 为空,为什么?因为有主键 id

2. undo日志

什么是undo日志?

一段内容缓冲区,用来存储日志数据。(主要用于回滚和隔离)

《MySQL》事务隔离性性的底层原理——MVCC_第1张图片

修改前,现将改行记录拷贝到undo log中,所以,undo log中就又有了一行副本数据。此时,新的副本,我们采用头插方式,插入undo log。

这样,我们就有了一个基于链表记录的历史版本链。所谓的回滚,无非就是用历史数据,覆盖当前数据。

上面的一个一个版本,我们可以称之为一个一个的快照

针对update,delete都是修改内容,都会形成版本链,如果是insert呢?因为insert是插入,也就是之前没有数据,那么insert也就没有历史版本。但是一般为了回滚操作,insert的数据也是要被放入undo log中,如果当前事务commit了,那么这个undo log 的历史insert记录就可以被清空了

最后,当没有其他事务访问数据的时候,undo log里的版本链才会被刷新释放。

3. ReadView

MySQL中的一个,用于管理事务并发情况下的可见性

  • 补充知识:select的查询分为当前读和快照读

当前读:就是查询的结果为最新版本记录。

# 当前读查询语句
select ... for update;

快照读:查询的结果为旧版本记录。

# 快照读查询语句(就是普通的select)
select ...;

在认识ReadView前,我们先对这个类中的重要成员先理解一下

字段 说明
m_ids 当前时间段活跃的事务集,是一个列表
up_limit_id 最小活跃事务id,即m_ids中的最小值
low_limit_id 全局事务id最大值+1
creator_trx_id 创建ReadView的事务id
  • 活跃事务:启动了,但未提交的事务

对于上面的成员,如图所示:

《MySQL》事务隔离性性的底层原理——MVCC_第2张图片

一个事务进行快照读的时候,才会创建ReadView对象。ReadView对象会记录当前事务的信息,包括在这之前完成的最大事务id(也就是正在运行的最小事务id),这一时间段内正在运行的所有事务和未开始的事务id

ReadView会根据事务的id大小给不同时间段内的事务访问不一样的事务版本内容,从而实现事务的隔离性。

RC和RR的区别

区别
RC:每次快照读都会生成一个新的ReadView读视图
RR:仅在第一次快照读时生成ReadView读视图,且不再改变

正是因为RC每次快照读都生成新ReadView对象,所以对象里面的数据也一直在更新,故会根据事务id筛选读到不同的版本,这就是不可重复读问题的由来。

你可能感兴趣的:(MySQL,mysql,数据库)