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链表。
相关文章:
mysql系列8—Innodb的undolog
背景 本文涉及的内容较为底层,做了解即可,是以前学习《高性能mysql》和《mysql是怎样运行的》的笔记整理所得。 undolog设计的初始目的是保证事务的原子性。mysql的修改操作发生后,如果所在的事务未被提交,如mysql服务或者操作系统…...
静默安装OGG for MySQL微服务版本,高效开展数据同步和迁移
一、背景 本文从Oracle GoldenGate微服务版的概念和组件介绍开始,从零介绍了怎么开始安装GoldenGate 21c for Oracle微服务版本的软件及部署。当然了,微服务版除新功能外包含传统版所有的功能。 二、安装部署 (一)下载OGG for …...
【Golang 面试题】每日 3 题(五十五)
✍个人博客:Pandaconda-CSDN博客 📣专栏地址:http://t.csdnimg.cn/UWz06 📚专栏简介:在这个专栏中,我将会分享 Golang 面试中常见的面试题给大家~ ❤️如果有收获的话,欢迎点赞👍收藏…...
PHP关键字入门指南:分类与功能全解析
如果你是刚接触PHP的新手,可能会对代码中那些“特殊单词”感到困惑。别担心!本文将用最通俗易懂的方式,带你认识PHP中的关键字——它们就像编程世界的“魔法咒语”,每个都有独特的作用。文末还附有代码示例,帮你快速上手! 一、什么是PHP关键字? PHP关键字是语言内置的特…...
消息中间件深度剖析:以 RabbitMQ 和 Kafka 为核心
在现代分布式系统和微服务架构的构建中,消息中间件作为一个不可或缺的组件,承担着系统间解耦、异步处理、流量削峰、数据传输等重要职能。尤其是在面临大规模并发、高可用性和可扩展性需求时,如何选择合适的消息中间件成为了开发者和架构师们…...
【万字详细教程】Linux to go——装在移动硬盘里的Linux系统(Ubuntu22.04)制作流程;一口气解决系统安装引导文件迁移显卡驱动安装等问题
Linux to go制作流程 0.写在前面 关于教程Why Linux to go?实际效果 1.准备工具2.制作步骤 下载系统镜像硬盘分区准备启动U盘安装系统重启完成驱动安装将系统启动引导程序迁移到移动硬盘上 3.可能出现的问题 3.1.U盘引导系统安装时出现崩溃3.2.不影响硬盘里本身已有…...
HCIA项目实践---OSPF的基本配置
9.5.12 OSPF的基本配置 (所搭环境如上图所示) A 先配置IP地址 (先进入路由器R1的0/0/0接口配置IP地址,再进入环回接口配置IP地址) (配置R2路由器的0/0/0和0/0/1以及环回接口的IP地址) (置R3路由器的0/0/0接…...
Vue 自动配置表单 el-switch等不常用组件覆盖默认值问题
有自动解析表单的vue组件如下,其原理是调用一个配置表单定义的接口,然后再调用获取表单配置的接口并将配置的数据覆盖表单的默认值。其中el-switch的配置值没有覆盖默认值,分析其原因。 主页面如下: <template> <div cla…...
零基础购买阿里云服务器,XShell连接云服务器
目录 1.环境搭建方式 2. 使用云服务器 3.使用终端软件登录到Linux 4.使用XShell登录主机 5.连接失败的原因: 下一篇更新:Linux的基础指令以及如何Linux的环境搭建 1.环境搭建方式 主要有四种: 1.直接安装在物理机上,虽然Linux有图形化…...
【系统架构设计师】虚拟机体系结构风格
目录 1. 说明2. 解释器体系结构风格3. 规则系统体系结构风格4. 例题4.1 例题1 1. 说明 1.p263。2.虚拟机体系结构风格的基本思想是人为构建一个运行环境,在这个环境之上,可以解析与运行自定义的一些语言,这样来增加架构的灵活性。3.虚拟机体…...
C语言中qsort函数使用技巧
在C语言的标准库中, qsort 函数是一个强大的通用排序函数,它采用快速排序算法,能够高效地对各种数据类型的数组进行排序。掌握 qsort 函数的使用技巧,对于提升程序的效率和代码的简洁性至关重要。 一、qsort函数基本介绍 qsort 函…...
WPF的Prism框架的使用
安装Prism.DryIoc库: Prism的区域和模块化: 一个区域可以显示一个用户控件 一个模块就是一个项目,也就是一个类库 动态切换用户控件的案例: <Grid><Grid.RowDefinitions><RowDefinition Height"auto"…...
LeetCode每日精进:142.环形链表II
题目链接:142.环形链表II 题目描述: 给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。 如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环…...
CPP集群聊天服务器开发实践(五):nginx负载均衡配置
1 负载均衡器的原理与功能 单台Chatserver可以容纳大约两万台客户端同时在线聊天,为了提升并发量最直观的办法需要水平扩展服务器的数量,三台服务器可以容纳六万左右的客户端。 负载均衡器的作用: 把client的请求按照负载均衡算法分发到具体…...
easyexcel解析excel文件的时候报错
easyexcel解析xls文件的时候,报错Exception in thread "main" com.alibaba.excel.exception.ExcelAnalysisException: java.lang.NoClassDefFoundError: org/objectweb/asm/Type at com.alibaba.excel.analysis.ExcelAnalyserImpl.analysis(ExcelAnalyser…...
Android设备 网络安全检测
八、网络与安全机制 6.1 网络框架对比 volley: 功能 基于HttpUrlConnection;封装了UIL图片加载框架,支持图片加载;网络请求的排序、优先级处理缓存;多级别取消请求;Activity和生命周期的联动(Activity结束生命周期同时取消所有网络请求 …...
word分栏使得最后一页内容自动平衡
word分栏使得最后一页内容自动平衡 Word中的分页符分节符 Word中的分页符与分节符统称为分隔符 【分页符】 是将一页内容分成两页, 但分离后的两页属于同一节;分页符用于强制在当前位置分页, 后续内容从下一页开始;分页符对应快捷键 Ctrl Enter ; 【分节符】 分节符用…...
完全免费稳定WebTerm网页版在线SSH连接,在线远程连接云服务器,可以控制背景,支持SFTP访问服务器文件。无需安装即可在线连接和管理服务器的SSH终端工具。支持跨平台设备。
目录 用途介绍 网页版SSH使用说明及教程 首次登录配置 设置中心介绍 编辑 SFTP功能 用途介绍 各位开发者在使用远程服务器时经常面临一个很致命的问题,就是当没有在使用自己电脑,远程服务器商家又没有提供在线的VNC连接,这时重新去安装…...
微信小程序医院挂号系统
第3章 系统设计 3.1系统体系结构 系统的体系结构非常重要,往往决定了系统的质量和生命周期。针对不同的系统可以采用不同的系统体系结构。本系统为微信小程序医院挂号系统,属于开放式的平台,所以在管理端体系结构中采用B/s。B/s结构抛弃了固…...
编程题-最大子数组和(中等-重点【贪心、动态规划、分治思想的应用】)
题目: 给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。 子数组是数组中的一个连续部分。 解法一(枚举法-时间复杂度超限): …...
Linux 文件类型,目录与路径,文件与目录管理
文件类型 后面的字符表示文件类型标志 普通文件:-(纯文本文件,二进制文件,数据格式文件) 如文本文件、图片、程序文件等。 目录文件:d(directory) 用来存放其他文件或子目录。 设备…...
golang循环变量捕获问题
在 Go 语言中,当在循环中启动协程(goroutine)时,如果在协程闭包中直接引用循环变量,可能会遇到一个常见的陷阱 - 循环变量捕获问题。让我详细解释一下: 问题背景 看这个代码片段: fo…...
以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:
一、属性动画概述NETX 作用:实现组件通用属性的渐变过渡效果,提升用户体验。支持属性:width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项: 布局类属性(如宽高)变化时&#…...
【位运算】消失的两个数字(hard)
消失的两个数字(hard) 题⽬描述:解法(位运算):Java 算法代码:更简便代码 题⽬链接:⾯试题 17.19. 消失的两个数字 题⽬描述: 给定⼀个数组,包含从 1 到 N 所有…...
镜像里切换为普通用户
如果你登录远程虚拟机默认就是 root 用户,但你不希望用 root 权限运行 ns-3(这是对的,ns3 工具会拒绝 root),你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案:创建非 roo…...
JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作
一、上下文切换 即使单核CPU也可以进行多线程执行代码,CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短,所以CPU会不断地切换线程执行,从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...
安宝特方案丨船舶智造的“AR+AI+作业标准化管理解决方案”(装配)
船舶制造装配管理现状:装配工作依赖人工经验,装配工人凭借长期实践积累的操作技巧完成零部件组装。企业通常制定了装配作业指导书,但在实际执行中,工人对指导书的理解和遵循程度参差不齐。 船舶装配过程中的挑战与需求 挑战 (1…...
代码随想录刷题day30
1、零钱兑换II 给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。 请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。 假设每一种面额的硬币有无限个。 题目数据保证结果符合 32 位带…...
排序算法总结(C++)
目录 一、稳定性二、排序算法选择、冒泡、插入排序归并排序随机快速排序堆排序基数排序计数排序 三、总结 一、稳定性 排序算法的稳定性是指:同样大小的样本 **(同样大小的数据)**在排序之后不会改变原始的相对次序。 稳定性对基础类型对象…...
JS手写代码篇----使用Promise封装AJAX请求
15、使用Promise封装AJAX请求 promise就有reject和resolve了,就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...
