InnoDB之Undo log格式
1. 前言
InnoDB有两大日志模块,分别是redo log和undo log。为了避免磁盘随机写,InnoDB设计了redo log,数据写入时只写缓冲页和redo log,脏页由后台线程异步刷盘,哪怕系统崩溃也能根据redo log恢复数据。但是我们漏了一种情况没有考虑,如果事务执行到一半系统崩溃了,redo log没刷盘还好,相当于本次事务的修改全部停留在内存里,重启后相当于什么也没做。但是,如果redo log已经刷盘了,MySQL重启后依然会根据redo log恢复页面,相当于本次事务执行到一半的状态,不符合原子性。为了保证原子性,MySQL必须撤销本次事务的所有修改,让本次事务「看起来什么都没做」,这就是undo log要负责的事情。
2. 事务回滚
事务回滚的需求是存在的,除了上述情况系统崩溃时的执行了一半的事务需要回滚,很多时候,开发者也经常需要通过命令ROLLBACK手动回滚事务。事务回滚后,该事务看起来什么都没做一样,它是符合原子性的。
如何实现事务回滚呢?想当然,肯定要把事务中修改的数据先记下来,比如:
- insert一条记录,就把主键记下来,回滚时删除该记录即可。
- delete一条记录时,把整条记录记下来,回滚时重新插入即可。
- update一条记录时,把对应列修改前的值全部记下来,回滚时修改回来即可。
- select不会修改记录,无需处理。
InnoDB其实也就是按照这个思路去设计的,每次对记录的修改,都会记一条日志,把回滚该条记录的必要数据给记录下来,这个日志就是undo log。
3. undo log格式
undo log是针对记录的,一般每对一条记录进行一次改动,都会生成1到2条undo log。一个事务在执行过程中,可能会修改很多记录,也就会生成若干条undo log,每个事务生成的undo log都会有一个唯一编号undo no,从0开始依次递增,undo no越小代表日志越早生成。
另外,undo log只针对聚簇索引,只有聚簇索引记录才有trx_id和roll_pointer隐藏列,二级索引是不会生成undo log的,MySQL在事务回滚时,会自动撤销对二级索引的变更。
roll_pointer隐藏列占用7个字节,组成如下:
| 属性 | 长度 | 说明 |
|---|---|---|
| is_insert | 1比特 | 是否是TRX_UNDO_INSERT大类 |
| rseg id | 7比特 | 回滚段id,最多128个回滚段 |
| Page Number | 4字节 | undo log所在页号 |
| Offset | 2字节 | undo log所在页号的偏移量 |
和redo log一样,InnoDB也设计了很多不同类型的undo log,增删改操作对应的undo log类型都不一样。
3.1 insert undo log
插入一条记录,对应的回滚操作就是删除该条记录,对应的undo log最需要记录的就是tableId和主键信息。InnoDB设计了TRX_UNDO_INSERT_REC类型的undo log来回滚insert操作。
| 属性 | 说明 |
|---|---|
| end of record | 本条undo log结束,下一条开始的位置 |
| undo type | undo log类型 |
| undo no | undo log序号 |
| table id | 表对应的id |
| 主键信息 | |
| <len,value>列表 | 主键各列的长度以及对应的值 |
| start of record | 上一条undo log结束,本条开始的位置 |
重点关注主键信息,假设表的主键是BIGINT类型的id,我们插入了一条id=10000的记录,那么主键信息存储的内容就是<8,10000>,如果主键包含多列,需要把每个列的长度和值都记录下来。
3.2 delete undo log
删除一条记录,对应的回滚操作就是把这条记录再重新插入回去,难道undo log要把一条用户记录完整的给记录下来吗?这未免也太浪费空间了,其实完全不需要这么做,这还得说回InnoDB删除记录的流程。
记录头信息里会有next_record属性,把记录按照主键串联成一条单向链表。页内被删除的记录也会根据该属性串联成一条单向链表,只不过这条链表的空间是可以被重用的,也称作「垃圾链表」。索引页Page Header里有PAGE_FREE属性,指向这条垃圾链表的头节点。记录头信息里还有delete_mark属性,用来标记记录是否被删除。

当我们要删除一条记录时,实际上会有两个阶段:
- 阶段1
将记录的delete_mark标记为1,记录undo log,写入trx_id和roll_pointer。事务提交前,记录一直处于这种中间状态,既不是正常记录,也不是已删除记录。只有将记录从正常链表中移除,加入到垃圾链表里,记录才算真正删除,其它事务也访问不到了。

为啥不直接删除记录,而是停留在中间状态?
这条记录还需要为MVCC服务,其它事务可能还需要访问。
- 阶段2
事务提交后,会有专门的线程来将记录真正的删除掉,这个过程称作「purge」。将记录从正常链表中移除,加入到垃圾链表,InnoDB采用头插法,PAGE_FREE会指向该记录,记录占用的空间也可以被重用了。与此同时,InnoDB还会修改Page Header里的PAGE_N_RECS、PAGE_GARBAGE、Page Directory等信息。

综上所述,事务提交前,只会经历阶段1,事务提交后也就不存在回滚了。所以针对delete操作,只需要把阶段1回滚即可,又因为阶段1记录其实并没有真正删除,所以undo log其实没必要保存完整记录。InnoDB设计了TRX_UNDO_DEL_MARK_REC类型的undo log。
| 属性 | 说明 |
|---|---|
| end of record | 本条undo log结束,下一条开始的位置 |
| undo type | undo log类型 |
| undo no | undo log序号 |
| table id | 表对应的id |
| info bits | 记录头信息的前4个比特位和record_type值 |
| old trx_id | 旧的事务id |
| old roll_pointer | 旧的回滚指针 |
| 主键信息 | |
| <len,value>列表 | 主键各列长度和值 |
| index_col_info len | 索引列信息总长度 |
| 索引各列信息 | |
| <pos,len,value> | 索引各列的位置、长度和值 |
| start of record | 上一条undo log结束,本条开始的位置 |
- 与insert不同的是,delete和update操作对应的undo log会记录下旧的
trx_id和roll_pointer,这样就可以找到上一次对记录修改时的undo log,这些undo log串联起来就是传说中的「版本链」,服务于MVCC。 - 根据主键信息定位到具体的记录,用户回滚时恢复。
- 索引各列信息主要用于purge阶段。
3.3 update undo log
update操作就比较复杂了,根据是否更新主键,InnoDB的处理方式也是不同的。
一、不更新主键
在不更新主键的前提下,如果更新后记录各列的长度与更新前相同,那么就可以「就地更新」,也就是直接在原有记录上进行更新,同时记录下undo log。
注意:是每个列的长度都和更新前相同,而非记录总长度和更新前相同。
就地更新的条件还是比较苛刻的,如果更新后列的长度发生变化,那么InnoDB会采用「先删除旧记录,再插入新记录」的方式来做更新,这里的“删除”是真的将记录删除并移入垃圾链表,而非仅仅打删除标记。
为什么会这么做呢?
在索引页里记录与记录之间是紧密无间的存储在一起的,中间没有空间,如果更新后记录占用的空间变大压根就没法存储,只能删掉重新申请空间插入一条。
总之,针对这种不更新主键的情况,InnoDB设计了TRX_UNDO_UPD_EXIST_REC类型的undo log。
| 属性 | 说明 |
|---|---|
| end of record | 本条undo log结束,下一条开始的位置 |
| undo type | undo log类型 |
| undo no | undo log序号 |
| table id | 表对应的id |
| info bits | 记录头信息的前4个比特位和record_type值 |
| old trx_id | 旧的事务id |
| old roll_pointer | 旧的回滚指针 |
| 主键信息 | |
| <len,value>列表 | 主键各列长度和值 |
| n_updated | 更新的列的数量 |
| <pod,ole_len,old_val>列表 | 更新列的旧值 |
| index_col_info len | 索引列信息总长度 |
| 索引各列信息 | |
| <pos,len,value> | 索引各列的位置、长度和值 |
| start of record | 上一条undo log结束,本条开始的位置 |
二、更新主键
针对update操作更新了主键的情况,InnoDB分为两个阶段来处理:
- 将旧记录进行delete mark操作,服务于MVCC。
- 根据更新后各列的值构建一条新记录并插入。
这两个阶段,对应两条undo log,也就是上面说的TRX_UNDO_DEL_MARK_REC和TRX_UNDO_INSERT_REC。
4. 对覆盖索引查询的影响
聚簇索引记录会有trx_id和roll_pointer隐藏列,通过undo log里的roll_pointer串联形成版本链,即一条记录存在多个版本,在select时会判断哪些版本对当前事务可见。
但是undo log只针对聚簇索引,二级索引没有roll_pointer,也不会生成undo log。我们又知道,InnoDB有个查询优化叫「覆盖索引查询」,即直接扫描二级索引返回结果,不再根据主键回表查询,可以大大提高数据查询的效率。
这时就存在一个问题,覆盖索引查询时,无法判断二级索引记录是否对当前事务可见!
InnoDB的解决方案是,在Page Header里有一个属性叫PAGE_MAX_TRX_ID,它代表修改当前页的最大事务id,如果PAGE_MAX_TRX_ID小于当前活跃的最小事务id,代表修改当前页的事务都提交了,可以直接使用覆盖索引查询,无需回表。反之,就需要回表根据聚簇索引的trx_id和roll_pointer以及对应的undo log来判断哪些二级索引记录是对当前事务可见的。
相关文章:
InnoDB之Undo log格式
1. 前言 InnoDB有两大日志模块,分别是redo log和undo log。为了避免磁盘随机写,InnoDB设计了redo log,数据写入时只写缓冲页和redo log,脏页由后台线程异步刷盘,哪怕系统崩溃也能根据redo log恢复数据。但是我们漏了一…...
一问学习StreamAPI终端操作
Java Stream管道流是用于简化集合类元素处理的java API。 在使用的过程中分为三个阶段: 将集合、数组、或行文本文件转换为java Stream管道流管道流式数据处理操作,处理管道中的每一个元素。上一个管道中的输出元素作为下一个管道的输入元素。管道流结果…...
在屎山代码中快速找到想要的代码法-锁表法(C#)
由于本人水平有限,文中有写得不对的地方请指正,本文的方法有些投机取巧,实在是没招的情况下可以酌情使用,如有侵权,请联系删除。 前几天接到一个需求,要在医嘱签署时对检验项目进行分方操作,分…...
网页设计html期末大作业
网页设计html期末大作业网页设计期末大作业-自制网站大一期末作业,外卖网站设计网页设计期末大作业-精美商城-首页框架网页设计期末大作业-自制网站 有导航栏,轮播图,按钮均可点进去,如下图所示 点我下载资源》》》》 大一期末…...
实战打靶集锦-006-Stapler
**写在前面:**记录博主的一次打靶经历。 目录1. 主机发现2. 端口发现3. 服务枚举4. 服务探查4.1 FTP探查4.1.1 匿名登录4.1.2 Elly用户4.1.3 John用户4.1.4 EXP搜索4.2 dnsmasq探查4.2.1 基础信息获取4.2.2 EXP搜索4.3 WEB应用探查4.3.1 浏览器访问4.3.2 目录扫描4.…...
致远OAA6版安装
准备工作,操作系统winserver2019,sqlserver2019。致远OA安装包0.SeeyonInstall.zip相关下载:winserver2019下载地址:cn_windows_server_2019_updated_july_2020_x64_dvd_2c9b67da.iso magnet:?xturn:btih:22A410DEA1B0886354A34D…...
python实用脚本(六)—— pandas库的使用(生成、读取表格)
本期主题: python的pandas使用 往期链接: python实用脚本(一)—— 批量修改目标文件夹下的文件名python实用脚本(二)—— 使用xlrd读取excelpython实用脚本(三)—— 通过有道智云AP…...
字符集、ASCII、GBK、UTF-8、Unicode、乱码、字符编码、解码问题等
编码解码一、背景二、字符的相关概念三、字符集3.1 ASCII[ˈski]3.1.1 ASCII的编码方式3.1.2 EASCII3.2 GBK3.2.1 GB 2312-803.2.2 GBK的制订3.2.3 GBK的实现方式3.3 Unicode(统一码、万国码)3.3.1 Unicode的出现背景3.3.2 Unicode的编写方式3.3.3 Unico…...
Java 布隆过滤器
你在么?在!一定在么?不在!一定不在么? 你想要100%的准去性,还是99%的准确性附带较高的速度和较小的资源消耗。 任何算法,任何经营收到的背后,都是时间效益 资源消耗 准确性的平衡&am…...
vscode连接服务器(腾讯云)
文章目录1. vscode远程总是报错2. vscode能连上腾讯云但密码不对或者登录后不能打开文件或文件夹1. vscode远程总是报错 报错如图所示 Could not establish connection to *** 过程试图写入的管道不存在。 在百度、csdn找了好久都是说删掉.ssh文件下的某个文件但我压根没有&a…...
IOS崩溃文件符号化实践
1.背景与项目难点 1.1 背景 由于公司之前使用的友盟要收费,filebase服务由谷歌提供,存在数据合规风险。需要实现稳定性分析功能,通过支持app崩溃信息实时采集、实时上报、实时自动解析并定位出代码问题,帮助研发同学及时定位崩溃…...
设计模式之适配器模式与桥接模式详解和应用
目录1 适配器模式1.1 定义1.2 应用场景1.3 适配器角色1.4 类适配器1.5 对象适配器1.5 接口适配器1.6 实战1.7 源码1.8 适配器与装饰器的对比1.9 适配器模式的优缺点1.10 总结2 桥接模式2.1 原理解析2.2 角色2.3 通用写法2.4 应用场景2.5 业务场景中的运用2.6 源码2.7 桥接模式优…...
Winform控件开发(14)——NotifyIcon(史上最全)
前言: 先看个气泡提示框的效果: 代码如下: 在一个button中注册click事件,当我们点击button1时,就能显示气泡 private void button1_Click(object sender, EventArgs e){notifyIcon1.Visible = true;notifyIcon1...
Verilog 学习第四节(从计数器到可控制线性序列机——LED实验进化六部曲)
从计数器到可控制线性序列机——LED实验进化六部曲一:让LED灯按照亮0.25s,灭0.75s的状态循环亮灭二:让LED灯按照亮0.25s,灭0.5s,亮0.75s,灭1s的状态循环亮灭三:让LED灯按照指定的亮灭模式亮灭&a…...
操作SSH无密登录配置
例如小编有三台服务器需要相互访问,就需要配置三台,这三台分别是hadoop102,hadoop103 , hadoop1041.打开三个服务器,分别生成hadoop102,hadoop103 , hadoop104的公钥和私钥输入命令,然后一直回车,这时候什么…...
Websocket详细介绍
需求背景 在某个资产平台,在不了解需求的情况下,我突然接到了一个任务,让我做某个页面窗口的即时通讯,想到了用websocket技术,我从来没用过,被迫接受了这个任务,我带着浓烈的兴趣,就…...
大数据书单(100本)
大数据书单(100本) 序号 书名 作者 出版社 1 Hadoop权威指南:大数据的存储与分析(第4版)(修订版)(升级版) Tom White 清华大学出版社 2 Hive编程指南 卡普廖洛 (Edward Capriolo) / 万普勒 (Dean Wampler) / 卢森格林 (Jason Rutherglen) / 曹坤 人民邮…...
python实战应用讲解-【语法基础篇】初识Python(附示例代码)
目录 前言 Python基础 基本概念: 为什么使用Python? Python2.x与3.x版本区别...
【2023保研夏令营】网安、CS(西交、华师、科、南等)
文章目录一、基本情况二、投递和入营情况三、考核情况1. 西交软院(面试)2. 川大网安(笔试面试)3. 华东师范数据学院(机试面试)4. 人大信息学院专硕(机试面试,保密)5. 南大…...
Qt COM组件导出源文件
文章目录摘要dumpcpp.exe注册COM组件COM 组件转CPP参考关键字: Qt、 COM、 组件、 源文件、 dumpcpp摘要 由于厂家提供的库不是纯净C库,是基于COM组件开的库,在和厂家友好交流无果下,只能研究下Qt 如何调用,好在Qt 的…...
网络编程(Modbus进阶)
思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...
【Linux】shell脚本忽略错误继续执行
在 shell 脚本中,可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行,可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令,并忽略错误 rm somefile…...
简易版抽奖活动的设计技术方案
1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...
《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》
引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...
高防服务器能够抵御哪些网络攻击呢?
高防服务器作为一种有着高度防御能力的服务器,可以帮助网站应对分布式拒绝服务攻击,有效识别和清理一些恶意的网络流量,为用户提供安全且稳定的网络环境,那么,高防服务器一般都可以抵御哪些网络攻击呢?下面…...
docker 部署发现spring.profiles.active 问题
报错: org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...
USB Over IP专用硬件的5个特点
USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中,从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备(如专用硬件设备),从而消除了直接物理连接的需要。USB over IP的…...
【生成模型】视频生成论文调研
工作清单 上游应用方向:控制、速度、时长、高动态、多主体驱动 类型工作基础模型WAN / WAN-VACE / HunyuanVideo控制条件轨迹控制ATI~镜头控制ReCamMaster~多主体驱动Phantom~音频驱动Let Them Talk: Audio-Driven Multi-Person Conversational Video Generation速…...
安全突围:重塑内生安全体系:齐向东在2025年BCS大会的演讲
文章目录 前言第一部分:体系力量是突围之钥第一重困境是体系思想落地不畅。第二重困境是大小体系融合瓶颈。第三重困境是“小体系”运营梗阻。 第二部分:体系矛盾是突围之障一是数据孤岛的障碍。二是投入不足的障碍。三是新旧兼容难的障碍。 第三部分&am…...
【从零学习JVM|第三篇】类的生命周期(高频面试题)
前言: 在Java编程中,类的生命周期是指类从被加载到内存中开始,到被卸载出内存为止的整个过程。了解类的生命周期对于理解Java程序的运行机制以及性能优化非常重要。本文会深入探寻类的生命周期,让读者对此有深刻印象。 目录 …...
