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

MySQL Undo Log

总结自bojiangzhou

undo log称为撤销日志或回滚日志。在一个事务中进行增删改操作时,都会记录对应的 undo log。在对数据库进行修改前,会先记录对应的 undo log,然后在事务失败或回滚的时候,就可以用这些 undo log 来将数据回滚到修改之前的样子。

InnoDB 在内存维护了一个全局变量来表示事务ID,每当要分配一个事务ID时,就获取这个变量值,然后把这个变量自增1

在行记录格式中行记录格式,行记录中会有三个隐藏列:

  • DB_ROW_ID:如果没有为表显式的定义主键,并且表中也没有定义唯一索引,那么InnoDB会自动为表添加一个row_id的隐藏列作为主键。

  • DB_TRX_ID:事务中对某条记录做增删改时,就会将这个事务的事务ID写入trx_id中。

  • DB_ROLL_PTR:回滚指针,本质上就是指向 undo log 的指针。

Undo Log Type

每对一条记录做一次改动,就会产生1条或者2undo log。一个事务中可能会有多个增删改SQL语句,这些 undo log 会被从 0 开始递增编号,这个编号称为 undo no

insert

插入一条数据对应的undo操作其实就是根据主键删除这条数据就行了。所以 insert 对应的 undo log 主要是把这条记录的主键记录上。

比如我们开启了一个事务,向 account 中插入两条数据:

BEGIN; 
INSERT INTO account(id,card,balance) VALUES (1, 'AA', 0),(2, 'BB', 0);

假设这个事务的事务ID为100,这条INSERT语句会插入两条数据,就会产生两个 undo log。插入记录的时候,会在行记录的隐藏列事务ID中写入当前事务ID,并产生 undo log,记录中的回滚指针会保存 undo log 的地址。而同一个页中的多条记录会通过next_record连接起来形成一个单链表,这块可以参考前面的行记录格式和数据页结构相关的文章。

delete

删除一条数据大致可以分为两个阶段:

  • 阶段一

首先是用户线程执行删除时,会先将记录头信息中的 delete_mask 标记为 1,而不是直接从页中删除,因为可能其它并发的事务还需要读取这条数据。(后面讲MVCC的时候就知道为什么了)

  • 阶段二

提交事务后,后台有一个 purge 线程会将数据真正删除。

首先要知道,页中的数据是通过记录头信息中的 netx_record 连接起来的单向链表(假设这个链表称为数据链表)。页中还有另一个链表,称为垃圾链表,记录真正删除后,会从数据链表中移除,然后加入到垃圾链表的头部,以便重用空间。

所以阶段二就是将记录从数据链表移除,加入到垃圾链表的头部。

也就是说,删除操作在事务提交前,只会经历阶段一,就是将记录的 delete_mask 标记为 1

此时接着执行一条删除的SQL语句,将id=2的这条数据删除:

BEGIN;
INSERT INTO account(id,card,balance) VALUES (1, 'AA', 0),(2, 'BB', 0);
DELETE FROM account WHERE id = 2;

因为是在同一个事务中,所以记录中的隐藏列trx_id没变,记录头中的delete_mask则标记为1了。然后生成了一个新的 undo log,并保存了记录中原本的trx_idroll_pointer,所以这个新的 undo log 就指向了旧的 undo log,而记录中的 roll_pointer 则指向这个新的 undo log。注意 undo log 中的事务编号也在递增。

update

在更新一条记录时,要把被更新的列的旧值记下来,这样之后回滚时再把这些列更新为旧值就好了。

Undo Log存储

undo log 分类

前边介绍了几种类型的 undo log,它们其实被分为两个大类来存储:

  • TRX_UNDO_INSERT

类型为 TRX_UNDO_INSERT_REC 的 undo log 属于此大类,一般由 INSERT 语句产生,或者在 UPDATE 更新主键的时候也会产生。

  • TRX_UNDO_UPDATE

除了类型为 TRX_UNDO_INSERT_REC 的 undo log,其他类型的 undo log 都属于这个大类,比如 TRX_UNDO_DEL_MARK_REC 、 TRX_UNDO_UPD_EXIST_REC ,一般由 DELETE、UPDATE 语句产生。

之所以要分成两个大类,是因为不同大类的 undo log 不能混着存储,因为类型为TRX_UNDO_INSERT_REC的 undo log 在事务提交后可以直接删除掉,而其他类型的 undo log 还需要提供MVCC功能,不能直接删除。

undo 页面链表

undo log 是存放在FIL_PAGE_UNDO_LOG类型的页中,一个事务中可能会产生很多 undo log,也许就需要申请多个undo页,所以 InnoDB 将其设计为一个链表的结构,将一个事务中的多个undo页连接起来。

但是前面说了 undo log 分为两大类,不能混着存储,所以如果事务中产生了这两大类型的 undo log,会创建两个链表,一个用来存储 TRX_UNDO_INSERT 类别的 undo log,一个用来存储 TRX_UNDO_UPDATE 类别的 undo log。

如果事务中还修改了临时表,InnoDB规定对普通表和临时表修改产生的 undo log 要分开存储,所以在一个事务中最多可能会有4个 undo 页面链表。

需要注意的是这些链表并不是事务一开始就分配好的,而是在需要某个类型的链表的时候才会去分配。

回滚段

redo log 是存放在重做日志文件中的,而 undo log 默认是存放在系统表空间中的一个特殊段(segment)中,这个段称为回滚段(Rollback Segment),链表中的页面都是从这个回滚段里边申请的。

InnoDB定义了128个回滚段(Rollback Segment),也就有128 Rollback Segment Header,每个Rollback Segment Header页面都对应着一个回滚段。一个 Rollback Segment Header 页面中包含1024undo slot,每个 undo slot 存放了 undo 链表头部的 undo 页的页号。就有128*1024=131072undo slot,也就是说最多同时支持131072个并发事务执行。

在系统表空间的第5号页面中存储了这128Rollback Segment Header页面地址。

事务回滚

前面在一个事务中增删改产生的一系列 undo log,都有 undo no 编号的。在回滚的时候,就可以应用这个事务中的 undo log,根据 undo no 从大到小开始进行撤销操作,就将数据还原为原来的样子了。

但需要注意的是,undo log 是逻辑日志,只是将数据库逻辑地恢复到原来的样子。所有修改都被逻辑地取消了,但是数据结构和页本身在回滚之后可能大不相同。因为同时可能很多并发事务在对数据库进行修改,因此不能将一个页回滚到事务开始的样子,因为这样会影响其他事务正在进行的工作。

MVCC

另一篇博客

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 【MySQL】MySQL索引失效场景
  • AbyssFish单连通周期边界多孔结构2D软件
  • 【从零开始实现stm32无刷电机FOC】【理论】【3/6 位置、速度、电流控制】
  • c++:面向对象的继承特性
  • 防止应用调试分析IP被扫描加固实战教程
  • 随身WiFi市场乱象横生,随身WiFi测评最好的格行随身WiFi如何引领变革?
  • 【Oracle】Oracle数据库中的数据类型
  • LabVIEW自动测控与故障识别系统
  • 基于 KV Cache 实现流式 Self-Attention 序列解码
  • 镍氢电池性能不减,你敢信?
  • 前端发布项目后,解决缓存的老版本文件问题
  • MFC常见问题解决
  • 3个方法教你如果快速绕过Excel工作表保护密码
  • 【ARM 常见汇编指令学习 7.1 -- LDRH 半字读取指令】
  • 时间处理的未来:Java 8全新日期与时间API完全解析
  • 2017 前端面试准备 - 收藏集 - 掘金
  • C# 免费离线人脸识别 2.0 Demo
  • canvas 高仿 Apple Watch 表盘
  • CoolViewPager:即刻刷新,自定义边缘效果颜色,双向自动循环,内置垂直切换效果,想要的都在这里...
  • IDEA常用插件整理
  • javascript 哈希表
  • java取消线程实例
  • Java应用性能调优
  • Magento 1.x 中文订单打印乱码
  • MySQL的数据类型
  • Python学习之路16-使用API
  • 构建工具 - 收藏集 - 掘金
  • 函数式编程与面向对象编程[4]:Scala的类型关联Type Alias
  • 快速体验 Sentinel 集群限流功能,只需简单几步
  • 那些被忽略的 JavaScript 数组方法细节
  • 适配mpvue平台的的微信小程序日历组件mpvue-calendar
  • Nginx惊现漏洞 百万网站面临“拖库”风险
  • shell使用lftp连接ftp和sftp,并可以指定私钥
  • ###C语言程序设计-----C语言学习(6)#
  • #{}和${}的区别是什么 -- java面试
  • #中的引用型是什么意识_Java中四种引用有什么区别以及应用场景
  • (03)光刻——半导体电路的绘制
  • (12)Hive调优——count distinct去重优化
  • (12)Linux 常见的三种进程状态
  • (a /b)*c的值
  • (LNMP) How To Install Linux, nginx, MySQL, PHP
  • (Matalb分类预测)GA-BP遗传算法优化BP神经网络的多维分类预测
  • (附源码)计算机毕业设计SSM基于java的云顶博客系统
  • (过滤器)Filter和(监听器)listener
  • (七)微服务分布式云架构spring cloud - common-service 项目构建过程
  • (四)stm32之通信协议
  • (原创)boost.property_tree解析xml的帮助类以及中文解析问题的解决
  • .bat批处理(十):从路径字符串中截取盘符、文件名、后缀名等信息
  • .NET Core SkiaSharp 替代 System.Drawing.Common 的一些用法
  • .net core 调用c dll_用C++生成一个简单的DLL文件VS2008
  • .NET MAUI Sqlite程序应用-数据库配置(一)
  • .NET Standard 支持的 .NET Framework 和 .NET Core
  • .Net6 Api Swagger配置
  • .net反编译的九款神器
  • .NET之C#编程:懒汉模式的终结,单例模式的正确打开方式