当前位置: 首页 > article >正文

mysql系列8—Innodb的undolog

背景

本文涉及的内容较为底层,做了解即可,是以前学习《高性能mysql》和《mysql是怎样运行的》的笔记整理所得。

undolog设计的初始目的是保证事务的原子性。mysql的修改操作发生后,如果所在的事务未被提交,如mysql服务或者操作系统发送了异常或执行了回滚等情况,事务的已修改操作需被还原。而还原需记录必要的数据,这些数据就是undolog。针对插入、删除、修改操作,undolog需要记录的信息量不同:
[1] 新增: 需要把主键记录下来,回滚时,删除主键对应的记录;
[2] 删除: 保存整条记录,回滚时,重新插入记录;
[3] 修改: 保存修改前后的记录,回滚时使用旧值替换新值。
通过特定的设计,可以减少上述信息量的存储,以提高资源使用效率。如mysql实现删除操作时,会通过delete_mark标记已删除(中间状态),对应的undolog日志中就不需要存储完整的记录。

1.undo日志内容

不同操作类型的undolog存储的数据量不同,为减少undolog存储空间,mysql分别设计了对应的数据存储格式,本章将以Insert为例介绍。
有几个概念需要提前了解。
[1] undo ID: 每条Undo日志对应一个ID。
[2] table ID: 每个表都对应一个ID, 可通过innodb_tables查询表ID信息,如:

mysql> SELECT NAME, TABLE_ID, SPACE, ROW_FORMAT, SPACE_TYPE FROM information_schema.innodb_tables WHERE name='test/t_student';
+----------------+----------+-------+------------+------------+
| NAME           | TABLE_ID | SPACE | ROW_FORMAT | SPACE_TYPE |
+----------------+----------+-------+------------+------------+
| test/t_student |    16148 | 15086 | Redundant  | Single     |
+----------------+----------+-------+------------+------------+

此时表ID为16148.

[3] db_trx_id和db_roll_pointer
每条记录包含3个隐藏列,其中两个是db_trx_id和db_roll_pointer,分别表示事务ID和回滚指针。
db_trx_id(事务ID)表示当前记录被新增或者最后一次修改对应的事务ID。
其中,事务ID是一个不断递增的全局变量;事务只有发送修改时,才会被分配一个事务ID,每个事务的ID全局唯一。
db_roll_pointer(回滚指针)表示指向该条记录对应的undo日志(下文介绍)。

[4] next_record属性
在[mysql系列2—InnoDB数据存储方式]介绍过: mysql表的每条记录都有一个指向下一条记录的指针,记录之间通过next_record形成链表。

1.1 undo日志格式

undo日志格式格式如下图所示:
在这里插入图片描述
包含四个部分:
[1] 边界信息
end of record存储本条undo日志的结束位置;start of end存储本条undo日志的起始位置;二者用于确定该条undo日志的边界。

[2] undo type
undo type记录了当前undo日志的类型信息; 不同的类型确定了不同的日志内容存储格式。

[3] undo ID和table ID
undo ID 表示该Undo的全局唯一ID,table ID表示修改操作涉及的表的ID。

[4] 日志信息
存储undo日志信息的数据部分,mysql根据undo type确定日志格式和对应的解析逻辑。Insert、Update、Delete操作在这部分存储格式有所区别。

1.2 insert操作

mysql记录Insert类型的Undo日志,只需要保存主键信息即可,如下图所示:
在这里插入图片描述
undo日志部分中存放了主键的各列的长度和实际的数值。
以下结合案例进行说明。
创建一个t_student表:

CREATE TABLE `t_student` (
	`id` INT(10) NOT NULL COMMENT '学号,唯一ID',
	`name` VARCHAR(50) NOT NULL COMMENT '姓名',
	`country` VARCHAR(32) NULL DEFAULT NULL,
	PRIMARY KEY (`id`) USING BTREE,
	UNIQUE INDEX `unix_name` (`name`) USING BTREE
)
ENGINE=InnoDB
;

包含一个主键索引和一个唯一索引;
开启一个事务(事务ID为100),向t_student其中插入两条数据:

start transaction;

INSERT INTO `t_student` (`id`, `name`, `country`) VALUES (1, 'sy', '中国');
INSERT INTO `t_student` (`id`, `name`, `country`) VALUES (2, 'mf', '澳大利亚');

从information_schema.innodb_tables表中查询得到t_student的table ID为16148.
此时数据记录和undo日志如下所示:
在这里插入图片描述
两个插入操作对应的类型为trx_undo_insert_rec;undo no存放的是undo日志ID, 分别为100和101;table ID存储的是t_student的table ID为16148; 数据部分存储主键大小和实际的值,整形长度为4;主键数值分别为1和2.
为了下文表述方便,对上图的日志细节部分进行忽略,简化表示为:
在这里插入图片描述
图中插入的两条记录通过next_record形成了记录链;每条记录的db_roll_pointer指向了记录对应的Undo日志。
其中:id=1的undo日志ID为100(后续用undolog100表示);id=2的undo日志ID为101(后续用undolog101表示).

1.3 delete操作和update操作

insert和delete对每行记录的操作只会对应产生一条UNDO日志;update语句修改某条记录的主键时产生两条日志,否则产生一条日志。

delete和update操作对应的undo行为是恢复数据,因此需要在日志内容区域存储除主键外更多的信息,其中包括原记录对应的事务ID和Undo指针。以下结合案例进行说明。
当事务100执行插入操作后,继续执行update操作时:

UPDATE t_student SET country = '上海' WHERE id = 2;

在这里插入图片描述
update操作修改了主键为2的记录,对应的undo日志ID为102(后续用undolog102表示). undolog102中存在一个old_roll_pointer指针,指向了undolog101.
当事务100执行update操作后,继续执行delete操作时:

delete from t_student WHERE id = 2;

在这里插入图片描述
delete操作删除了主键为2的记录,减delete_mark标记为1表示该记录已被删除;对应的undo日志ID为103(后续用undolog103表示). undolog103中存在一个old_roll_pointer指针,指向了undolog102.
说明:也可以提交事务100后(不提交会因行锁冲突阻塞更新),再开启一个事务执行更新或者删除操作,效果与案例相似(仅保存的旧事务ID不别)。

1.4 版本链

insert语句在事务提交后UNDO日志会被删除;而delete和update类型的UNDO日志会参与MVCC,即使所在事务提交,也不会立即删除(而是后续有单独的线程完成清理)。

上一节中id为2的记录对应的undolog通过roll_ptr指针相连,将其单独抽出来得到一个链表,叫做版本链。
在这里插入图片描述

2.undo页

前面已经介绍了undo日志格式,undo日志会按照格式存放到FIL_PAGE_UNDO_LOG (undo页)中,undo页的结构如下所示:
在这里插入图片描述
File Header和File Trailer是页的固定格式,在介绍数据页时已经介绍过,这里关注一下Undo Page Header, 包括如下4个部分:
[1] type: Undo日志大类,Insert类型和其他类型(delete和update类型);
[2] page_start: 第一条undo日志的起始位置;
[3] page_free: 下一条undo日志的可写入位置;
[4] node信息: 存储链表信息,在下一章中介绍。

3.undo链表

一个事务可能有多个操作命令,每个操作命令可能修改多个行;即一个事务在执行过程中可能生成多个Undo日志,因此一个undo页可能写不完,所以mysql引入了一个undo链的概念。
在这里插入图片描述
上一章节中node存储的就是串联undo页之间的指针信息,undo页通过node信息形成双向链表。
mysql整体上将Undo日志分为了两类: insert类型和其他类型(update和delete)。Insert类型的Undo日志在事务提交后可以直接删除;而其他类型的undo日志参与MVCC,不能直接删除,而是由后续专门的线程负责清理。因此,为了提高效率,mysql将不同类型的日志存放到在不同的undo页中。
说明:并不是事务创建时就分配undo链,而是按需分配,发送数据修改时才会分配。
每个事务可能有多条链:
在这里插入图片描述
除此之外,对于临时表,还有单独的两个链表(原理相同,本文不做介绍)。
最后,为了提高undo日志写入效率,不同的事务在执行过程中生成的undo日志存放到不同的undo页面链表中;即每个事务有自己单独的undo链表。


http://www.kler.cn/a/549644.html

相关文章:

  • MVC模式和MVVM模式
  • 【漫话机器学习系列】092.模型的一致性(Consistency of a Model)
  • 国产FPGA开发板选择
  • Spring Boot实战:拦截器
  • 2025百度快排技术分析:模拟点击与发包算法的背后原理
  • yarn add electron --dev --platform=win64 报错 certificate has expired 2025年 解决办法
  • 消息队列(MQ)核心知识与应用场景解析
  • oracle使用动态sql将多层级组织展平
  • git仓库拉取最新更改
  • 拉分支提示git变基全套解决方案
  • Day65_20250213图论part9_dijkstra(堆优化版)|Bellman_ford算法精讲
  • 神经网络新手入门(3)光明顶复出(2006-2012)
  • 网络共享基于什么原理,为什么MAC可以编辑局域网的windows系统文件?
  • 从零开始在Windows系统上搭建一个node.js后端服务项目
  • 基于Ubuntu+vLLM+NVIDIA T4高效部署DeepSeek大模型实战指南
  • SQL注入之布尔盲注+时间盲注
  • 【数据库维护】Clickhouse数据库维护关键系统表相关指标说明,支撑定位慢SQL及多实例场景下分析各实例运行情况
  • Qt使用pri和pro文件进行模块化编程
  • 机器学习·逻辑回归
  • 华为昇腾920b服务器部署DeepSeek翻车现场