全网最详细MVCC讲解,一篇看懂( 二 )


字段
含义
DB_ROW_ID
隐含的自增ID(隐藏主键),用于唯一标识表中的每一行数据,如果数据表没有主键 , InnoDB会自动以DB_ROW_ID产生一个聚簇索引 。
DB_TRX_ID
该字段存储了当前行数据所属的事务ID 。每个事务在数据库中都有一个唯一的事务ID 。通过 DB_TRX_ID 字段,可以追踪行数据和事务的所属关系 。
DB_ROLL_PTR
该字段存储了回滚指针(Roll Pointer),它指向用于回滚事务的Undo日志记录 。
Undo Log上文提到了 Undo 日志,这个 Undo 日志是 MVCC 能够得以实现的核心所在 。
Undo日志(Undo Log)是MySQL中的一种重要的事务日志,Undo日志的作用主要有两个方面:

  • 事务回滚:当事务需要回滚时,MySQL可以通过Undo日志中的旧值将数据还原到事务开始之前的状态,保证了事务回滚的一致性 。
  • MVCC实现:MVCC 是InnoDB存储引擎的核心特性之一 。通过使用Undo日志,MySQL可以为每个事务提供独立的事务视图,使得事务读取数据时能看到一致且符合隔离级别要求的数据版本 。
在InnoDB存储引擎中,Undo日志分为两种:插入(insert)Undo日志 和 更新(update)Undo日志
  • insert undo log:插入Undo日志是指在插入操作中生成的Undo日志 。由于插入操作的记录只对当前事务可见 , 对其他事务不可见,因此在事务提交后可以直接删除,无需进行purge操作 。
  • update undo log:更新Undo日志是指在更新或删除操作中生成的Undo日志 。更新Undo日志可能需要提供MVCC机制,因此不能在事务提交时就立即删除 。相反,它们会在提交时放入Undo日志链表中,并等待purge线程进行最终的删除 。删除操作只是设置一下老记录的 DELETED_BIT , 并不真正将过时的记录删除,为了节省磁盘空间,InnoDB有专门的purge线程来清理 DELETED_BIT 为true的记录 。
注意:由于查询操作(SELECT)并不会修改任何记录,所以在查询操作执行时,并不需要记录相应的 undo log。
不同事务或者相同事务对同一记录行的修改 , 会使该记录行的 undo log 成为一条链表 , 链首就是最新的记录 , 链尾就是最早的旧记录
举个例子 , 比如有个事务A插入了一条新记录:insert into user(id, name) values(1, "小明')
现在来了一个事务B对该记录的name做出了修改 , 改为 "小王" 。
在事务B修改该行数据时,数据库会先对该行加排他锁 , 然后把该行数据拷贝到 undo log 中作为旧记录,即在 undo log 中有当前行的拷贝副本.
拷贝完毕后,修改该行name为 "小王,并且修改隐藏字段的事务ID为当前事务B的ID, 并将回滚指针指向拷贝到 undo log 的副本记录 , 即表示我的上一个版本就是它 , 事务提交后,释放锁 。
全网最详细MVCC讲解,一篇看懂

文章插图
图片
此时又来了个事务C修改同一个记录,将name修改为 "小红" 。
在事务C修改该行数据时,数据库也先为该行加锁,然后把该行数据拷贝到 undo log 中,作为旧记录,发现该行记录已经有 undo log 了 , 那么最新的旧数据作为链表的表头,插在该行记录的 undo log 最前面,如下图:
全网最详细MVCC讲解,一篇看懂

文章插图
图片
关于 DB_ROLL_PTR 与 Undo日志 的配合工作,具体流程如下:
  1. 在更新或删除操作之前 , MySQL会将旧值写入Undo日志中 。
  2. 当事务需要回滚时,MySQL会根据事务的Undo日志记录,通过 DB_ROLL_PTR 找到对应的Undo日志 。
  3. 根据Undo日志中记录的旧值,MySQL将旧值恢复到相应的数据行中,实现数据的回滚操作 。
比方说现在想回滚到事务B , name值为 "小王" 的时候,只需通过 DB_ROLL_PTR 顺着列表找到对应的 Undo日志,将旧值恢复到数据行即可 。
通过 DB_ROLL_PTR 和 Undo日志 的配合工作 , MySQL能够有效地管理事务的一致性和隔离性 。Undo日志的使用也使得MySQL能够支持MVCC,从而提供了高并发环境下的读取一致性和事务隔离性 。


推荐阅读