首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >MySQL 核心模块揭秘 | 53 期 | DELETE 删除的记录,ROLLBACK 怎么回滚?

MySQL 核心模块揭秘 | 53 期 | DELETE 删除的记录,ROLLBACK 怎么回滚?

作者头像
爱可生开源社区
发布于 2025-02-19 15:37:32
发布于 2025-02-19 15:37:32
16700
代码可运行
举报
运行总次数:0
代码可运行

作者:操盛春,爱可生技术专家,公众号『一树一溪』作者,专注于研究 MySQL 和 OceanBase 源码。

爱可生开源社区出品,原创内容未经授权不得随意使用,转载请联系小编并注明来源。


本文基于 MySQL 8.0.32 源码,存储引擎为 InnoDB。

正文

1. 准备工作

创建测试表:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
CREATE TABLE `t1` (
  `id` int unsigned NOT NULL AUTO_INCREMENT,
  `i1` int DEFAULT '0',
  PRIMARY KEY (`id`) USING BTREE,
  KEY `idx_i1` (`i1`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;

插入测试数据:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
INSERT INTO `t1` (`id`, `i1`) VALUES 
(10, 101), (20, 201), (30, 301), (40, 401);

删除 <id = 40> 的记录:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
DELETE FROM t1 WHERE id = 40;

回滚:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
ROLLBACK;

2. 读取 Undo 日志

回滚 Delete 操作过程中读取 Undo 日志,和回滚 Insert 操作过程中读取 Undo 日志的流程一样,这里不再赘述。

3. 解析 Undo 日志

准备工作中,Delete 语句删除 <id = 40> 的记录产生的 Undo 日志如下:

3.1 解析参数

删除记录产生的 Undo 日志的格式,除了没有更新字段区域,剩余的其它部分和更新记录产生的 Undo 日志的格式相同。删除记录产生的 Undo 日志的参数区域,和插入记录产生的 Undo 日志的参数区域的不同之处,可以参考前面关于回滚更新记录的内容。

删除记录产生的 Undo 日志的参数区域,对应图中 offset [339, 344)

  • type_flag,值为 78,由 14 | 64 得到。 14 在代码里定义为 TRX_UNDO_DEL_MARK_REC,表示这条 Undo 日志由删除记录产生,参数区域之后的其它属性按照 TRX_UNDO_DEL_MARK_REC 类型的 Undo 日志格式解析。 64 在代码里定义为 TRX_UNDO_MODIFY_BLOB,有这个标志就意味着 Undo 日志的参数区域包含 lob_flag 属性。
  • lob_flag,因为 type_flag 中设置了 TRX_UNDO_MODIFY_BLOB 标志,所以 Undo 日志中写入了 lob_flag 属性。这个属性值硬编码为 0x00,实际上没有使用。
  • undo_no,值为 0。说明这是 Update Undo 段中第一条 Undo 日志。Delete 操作产生的 Undo 日志,也会写入到 Update Undo 段管理的 Undo 页中。
  • table_id,值为 1067。这是 <id = 40> 的记录所属表的 ID。InnoDB 会用这个 ID 打开表,用于回滚过程中的后续操作。

3.2 解析头信息和隐藏字段

头信息和隐藏字段区域,对应图中 offset [344, 358),包含 3 个属性:

  • info_bits,值为 0。这是 <id = 40> 的记录的头信息中第 1 字节第 5 ~ 8 位的值。
  • DB_TRX_ID,值为 2846。这是 <id = 40> 的记录被当前回滚事务删除之前的 DB_TRX_ID 字段值。
  • DB_ROLL_PTR,值为 36310272004456759。这是 <id = 40> 的记录被当前回滚事务删除之前的 DB_ROLL_PTR 字段值。

解析出来之后,这 3 个属性的值会保存到回滚操作内存对象(undo_node)的 update 属性中。

回滚时,这 3 个属性的值分别被拷贝到 <id = 40> 的记录的头信息、DB_TRX_ID 和 DB_ROLL_PTR 字段中。

3.3 解析主键字段

主键字段区域,对应图中 offset [358, 363),包含 2 个属性:

  • primary_field_len,值为 4。表示 t1 表的主键字段值(id = 40)在 Undo 日志中占用 4 字节。
  • primary_field_value,值为 40。这是 <id = 40> 的记录的主键字段(id)值。

解析出来之后,主键字段值保存到回滚操作内存对象(undo_node)的 ref 属性中。

3.4 二级索引字段

二级索引字段区域,对应图中 offset [363, 377),存放的是 <id = 40> 的记录对应的所有二级索引记录的字段值。

因为每个二级索引记录的末尾都包含主键字段,所以,Undo 日志的这个区域中记录了 id 字段的信息。每组 <index_field_pos, index_field_len, index_field_value> 对应一个字段的信息。

这个区域存放的二级索引字段信息,回滚时不需要解析,因为用不到。purge 线程清理标记删除的二级索引记录时才会用到。

4. 查找主键索引记录

前面从 Undo 日志中解析主键字段值(id)得到 40,保存到了回滚操作内存对象(undo_node)的 ref 属性中。现在需要根据主键字段值去主键索引的 B+ 树中查找 <id = 40> 的记录。

找到记录之后,读取记录中所有字段值,保存到回滚操作内存对象(undo_node)的 row 属性中。另外,还会保存指向主键索引 B+ 树中 <id = 40> 的记录的指针,后面回滚这条主键索引记录时会用到。

undo_node 对象的 row 属性中,既包含我们创建表时指定的字段,也包含 InnoDB 自己加上的隐藏字段 DB_TRX_IDDB_ROLL_PTR。因为我们创建表时指定了主键,记录中不会包含隐藏字段 DB_ROW_ID。row 属性中保存的各字段值如下:

  • id = 40
  • i1 = 401
  • DB_TRX_ID = 2848
  • DB_ROLL_PTR = 281474985427281

以上这些是 t1 表中 <id = 40> 的记录被当前回滚事务标记删除之后的各字段值。其中 DB_TRX_ID 是当前回滚事务的 ID,DB_ROLL_PTR 是当前 Undo 日志的地址。

另外,<id = 40> 的记录的头信息中第 1 字节第 5 ~ 8 位也发生了变化,因为这条记录已经被当前回滚事务标记删除,标记删除对应的标志位会被设置为 1。

5. 回滚二级索引记录

Delete 操作删除 t1 表中 <id = 40> 的记录,会先删除主键索引记录,然后遍历所有二级索引,每一轮遍历删除一个二级索引中的对应主键索引中 <id = 40> 的二级索引记录。回滚时,则会按照相反的顺序来,先逐个回滚二级索引记录,最后才回滚主键索引记录。

t1 表中只有一个二级索引 idx_i1,回滚流程如下:

  • 根据回滚操作内存对象(undo_node)的 row 属性中保存的 i1 和 id 字段值,构造二级索引记录 <i1 = 401, id = 40>。
  • 找到 idx_i1 中 <i1 = 401, id = 40> 的记录。
  • 取消该记录的头信息中的删除标志。这条二级索引记录就恢复为正常记录了。

6. 回滚主键索引记录

前面构造主键索引记录时,已经找到了主键索引中 <id = 40> 的记录,也保存了指向主键索引 B+ 叶子结点中 <id = 40> 的记录的指针。回滚主键索引记录时,可以直接使用这个指针操作 <id = 40> 的记录。

用回滚操作内存对象(undo_node)的 update 属性中保存的 <id = 40> 的记录的信息,回滚主键索引记录的流程如下:

  • 用 update 属性中保存的 DB_TRX_IDDB_ROLL_PTR 两个字段的值,把 <id = 40> 的记录恢复到被当前回滚事务标记删除之前的状态。
  • 用 update 属性中保存的头信息,更新 <id = 40> 的记录的头信息中第 1 字节第 5 ~ 8 位,把记录的头信息恢复到被当前回滚事务标记删除之前的状态。这个操作,就是把 <id = 40> 的记录从标记删除记录恢复为正常记录。

7. 总结

回滚 Delete 操作产生的一条 Undo 日志的主要流程如下:

  • 读取一条 Undo 日志。
  • 解析 Undo 日志。
  • 查找主键索引记录。
  • 用主键索引中查询出来的各字段值,回滚二级索引记录。
  • 用头信息和隐藏字段区域解析出来的头信息和隐藏段的值,回滚主键索引记录。
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-02-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 爱可生开源社区 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
MySQL 核心模块揭秘 | 50 期 | Update 更新的记录,Rollback 怎么回滚?
回滚 Update 操作过程中读取 Undo 日志,和回滚 Insert 操作过程中读取 Undo 日志的流程一样,这里不再赘述。
爱可生开源社区
2025/01/23
1500
MySQL 核心模块揭秘 | 50 期 | Update 更新的记录,Rollback 怎么回滚?
MySQL 核心模块揭秘 | 52 期 | 删除记录的 Undo 日志
Delete 语句删除表中一条记录,先标记删除主键索引记录,再标记删除二级索引记录。事务提交之后,后台 purge 线程才会把标记删除的记录物理删除,从而最终完成从表中删除一条记录的流程。
爱可生开源社区
2025/02/12
1910
MySQL 核心模块揭秘 | 52 期 | 删除记录的 Undo 日志
MySQL八:读懂MVCC多版本并发控制
mysql在并发的情况下,会引起脏读,幻读,不可重复读等一系列的问题,为解决这些问题,引入了mvcc的机制。本文就详细看看mvcc是怎么解决脏读,幻读等问题的。
云扬四海
2022/09/26
8160
MySQL 核心模块揭秘 | 49 期 | 更新记录的 Undo 日志
作者:操盛春,爱可生技术专家,公众号『一树一溪』作者,专注于研究 MySQL 和 OceanBase 源码。
爱可生开源社区
2025/01/16
1210
MySQL 核心模块揭秘 | 49 期 | 更新记录的 Undo 日志
✅浅聊MVCC?
MVCC,即多版本并发控制(Multiversion Concurrency Control),类似于数据库锁,是一种优雅的并发控制方案。
@派大星
2024/05/02
2310
InnoDB MVCC 详解
提示:公众号展示代码会自动折行,建议横屏阅读 1. 概述 InnoDB支持MVCC(Multi-Version Concurrency Control), undo日志中保存了多版本的记录,undo支持事务回滚的同时,也支持数据的一致性读。undo日志保存在回滚段中,undo日志的回收由purge操作进行。 InnoDB行记录中保存了事务相关信息如事务id,roll_ptr。id用于可见性判断,roll_ptr用于从undo中回溯历史版本。一致性读会开启一个ReadView,ReadView包含当前正在执
腾讯数据库技术
2019/08/16
3.6K0
InnoDB MVCC 详解
MySQL 核心模块揭秘 | 47 期 | 插入记录的 Undo 日志
插入一条记录到表中,首先会插入记录到主键索引,然后遍历二级索引,把记录插入到各个二级索引中。
爱可生开源社区
2024/12/27
1370
MySQL 核心模块揭秘 | 47 期 | 插入记录的 Undo 日志
MySQL 核心模块揭秘 | 31 期 | 隐式锁
作者:操盛春,爱可生技术专家,公众号『一树一溪』作者,专注于研究 MySQL 和 OceanBase 源码。
爱可生开源社区
2024/09/14
1150
MySQL 核心模块揭秘 | 31 期 | 隐式锁
MySQL优化--MVCC
mvcc在MySQL的InnoDB引擎中的实现主要是为了提高并发性能,采用更加完善的方式处理读、写之间的冲突,即使有冲突时,也可以做到不加锁,非阻塞并发读
shimeath
2020/08/11
5931
MySQL 核心模块揭秘 | 13 期 | 回滚到 savepoint
每条 SQL 前面的数字是它的编号,9 条 SQL 分别为 SQL 1、SQL 2、...、SQL 9,其中,SQL 9 是本文的主角。
爱可生开源社区
2024/04/11
2610
MySQL 核心模块揭秘 | 13 期 | 回滚到 savepoint
MySQL是怎么读数据的——多版本并发控制
我在之前的文章中(【MySQL入门】之MySQL数据库的锁机制(一),【MySQL入门】之MySQL数据库的锁机制(二))介绍了MySQL的全局锁、表锁和行锁,今天我在来介绍下MySQL的一致性非锁定读、一致性锁定读。再说之前我们先思考个问题,当我们用mysqldump进行逻辑备份时,如果有事务对表进行修改操作,那么我们备份出来的数据是否包含修改后的数据呢?如果mysqldump备份出的数据不包含之后修改的数据,那么他又是怎么保存之前的数据的呢?
MySQL数据库技术栈
2020/08/04
8660
MySQL 高频面试题解析 第02期:当前读和快照读的区别
普通读(也称快照读,英文名:Consistent Read),就是单纯的 SELECT 语句,不包括下面这两类语句:
数据库交流
2022/04/25
4K3
MySQL 高频面试题解析 第02期:当前读和快照读的区别
数据库事务系列-MySQL跨行事务模型
说来和MySQL倒是有缘,毕业的第一份工作就被分配到了RDS团队,主要负责把MySQL弄到云上做成数据库服务。虽说整天和MySQL打交道,但说实话那段时间并没有很深入的理解MySQL内核,做的事情基本都是围绕着MySQL做管控系统,比较上层。好在周边都是MySQL内核神级人物,在他们的熏陶下多多少少对MySQL的一些基本知识有一些零碎的记录和模糊的认识,这些基础对于今天整理理解MySQL跨行事务模型非常重要。更重要的,有很多不解的地方也可以向大神请教。
Java_老男孩
2019/12/02
1.6K0
MySQL 核心模块揭秘 | 30 期 | 死锁日志详解
以上是从死锁日志中择(zhái)出来的辅助信息。只有系统变量 log_error_verbosity 的值为 3,死锁日志中才会包含这些信息。
爱可生开源社区
2024/09/14
2260
MySQL 核心模块揭秘 | 30 期 | 死锁日志详解
MySQL 之 MVCC 多版本并发控制
前面介绍了 MySQL 中面试常问到的锁,以及详细介绍了三种较为重要的行级别锁(间隙锁、记录锁、临键锁)。今天我们来介绍 MySQL 中 InnoDB 存储引擎为了在非锁定读下解决幻读问题,使用的 MVCC 机制。
用户6256742
2024/06/13
2050
MySQL 之 MVCC 多版本并发控制
Mysql关于锁方面和事务方面的问题
开启间隙锁, 间隙锁会封锁该条记录相邻两个键之间的空白区域,防止其它事务在这个区域内插入、修改、删除数据;所谓间隙是将数据分为不同区间,对该区间范围进行加锁,区间的规则为左开右闭,比如当数据为1,3,5时,对应的区间为(-∞,1],(1,3],(3,5],(5,+∞];
名字是乱打的
2022/05/13
6380
Mysql关于锁方面和事务方面的问题
MySQL InnoDB引擎
表空间是InnoDB存储引擎逻辑结构的最高层, 如果用户启用了参数 innodb_file_per_table(在8.0版本中默认开启) ,则每张表都会有一个表空间(xxx.ibd),一个mysql实例可以对应多个表空间,用于存储记录、索引等数据。
用户9615083
2022/12/25
1.5K0
MySQL InnoDB引擎
MySQL 核心模块揭秘 | 39 期 | 死锁案例分析(1)插入意向锁
作者:操盛春,爱可生技术专家,公众号『一树一溪』作者,专注于研究 MySQL 和 OceanBase 源码。
爱可生开源社区
2024/10/30
1610
MySQL 核心模块揭秘 | 39 期 | 死锁案例分析(1)插入意向锁
MYSQL MVCC 实现机理 与 知识漏洞要补
说来惭愧,被友人问了一些关于MYSQL 的问题,虽然算不上对答如流,但也算是沉稳应对。唯独折在 MVCC 中MYSQL INNODB 是如何实现的问题上,回答错误扣10分。本着知错能改,有漏洞要补的,精神。还是的把MVCC 以及MYSQL 怎么实现MVCC 的事情重新的学习一遍。
AustinDatabases
2019/09/04
8430
MYSQL MVCC 实现机理  与  知识漏洞要补
MySQL 回滚日志 undo log
InnoDB的已提交读和可重复读的底层实现原理:MVCC(多版本并发控制),MVCC提供了一种并发的读取方式,即快照读 ,同一份数据会有多个版本
终有救赎
2023/11/08
4450
MySQL 回滚日志 undo log
相关推荐
MySQL 核心模块揭秘 | 50 期 | Update 更新的记录,Rollback 怎么回滚?
更多 >
交个朋友
加入腾讯云官网粉丝站
蹲全网底价单品 享第一手活动信息
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档