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

MySQL系列—12.Undo log

1、概念

DML 操作导致数据变化 , 将变化前的记录写入 Undo 日志。

作用

用于记录更改前的一份 copy ,在操作出错时,可以用于回滚、撤销还原,只将数据库
逻辑地恢复到原来的样子
插入一条记录时,至少要把这条记录的主键值记下来,之后回滚的时候只需要把这个主键值对应的记录删掉就好了。(对于每个INSERT,InnoDB存储引擎会完成一个DELETE)
删除了一条记录,至少要把这条记录中的内容都记下来,这样之后回滚时再把由这些内容组成的记录插入到表中就好了。(对于每个DELETE,innoDB存储引擎会执行一个INSERT)
修改了一条记录,至少要把修改这条记录前的旧值都记录下来,这样之后回滚时再把这条记录更新为旧值就好了。(对于每个UPDATE,InnoDB存储引擎会执行一个相反的UPDATE,将修改前的行放回去)

存储位置

undo 存储在回滚段 ( Rollback Segment) ,每个回滚段记录了 1024 undo log segment

参数

innodb_undo_directory
设置 rollback segment文件所在的路径。这意味着 rollback segment可以存放在共享表空间以外的位置,即可以设置为 独立表空间。该参数的默认值为“./"”,表示当前InnoDB存储引擎的目录。
innodb_undo_tablespaces
设置构成 rollback segment文件的数量,设置该参数后,会在路径 innodb_undo_directory看到 undo为前缀的文件,该文件就代表 rollback segment文件。

回滚段与事务

    每一个事务只会使用一个回滚段,一个回滚段在同一个时刻可能会服务于多个事务。
    当一个事务开始的时候,会制定一个回滚段,在事务进行的过程中,当数据被修改时,原始的数据会被复制到回滚段。
    在回滚段中,事务会不断填充盘区,直到事务结束或所有的空间被用完。如果当前的盘区不够用,事务会在段中请求扩展下一个盘区,如果所有已分配的盘区都被用完,事务会覆盖最初的盘区或者在回滚段允许的情况下扩展新的盘区来使用。
    回滚段存在于undo表空间中,在数据库中可以存在多个undo表空间,但同一时刻只能使用一个undo表空间。

purge线程作用
( 1 ) 清理 undo 页和清除 page 里面带有 Delete_Bit 标识的数据行
( 2 )InnoDB 中,事务中的 Delete 操作实际上并不是真正的删除掉数据行,而是一种 Delete Mar
k 操作,在记录上标识 Delete_Bit ,而不删除记录。是一种 " 假删除 ", 只是做了个标记,真正的
删除工作需要后台 purge 线程去完成。

回滚段中的数据分类

    未提交的回滚数据(uncommitted undo information):该数据所关联的事务并未提交,用于实现读一致性,所以该事务不能被其他事务的数据所覆盖。
    已经提交但未过期的回滚数据(committed undo information) :该数据关联的事务已经提交,但是仍然受到undo retention参数的保持时间的影响。
    事务已经提交并过期的数据(expired undo information) :事务已经提交,而且数据保存时间已经超过undo retention参数指定的时间,属于已经过期的数据。当回滚段满了之后,会优先覆盖"事务已经提交并过期的数据"。

事务提交后并不能马上删除undo log及undo log所在的页。这是因为可能还有其他事务需要通过undo log来得到行记录之前的版本。故事务提交时将undo log放入一个链表中,是否可以最终删除undo log及undo log所在页由purge线程来判断。

undo的类型

insert undo log是指在insert操作中产生的undo log。因为insert操作的记录,只对事务本身可见,对其他事务不可见(这是事务隔离性的要求),故该undo log可以在事务提交后直接删除。不需要进行purge操作。

update undo log记录的是对delete和update操作产生的undo log。该undo log可能需要提供MVCC机制,因此不能在事务提交时就进行删除。提交时放入undo log链表,等待purge线程进行最后的删除。

详细生成过程

对于InnoDB引擎来说,每个行记录除了记录本身的数据之外,还有几个隐藏的列:

    DB_ROW_ID:如果没有为表显式的定义主键,并且表中也没有定义唯一索引,那么InnoDB会自动为表添加一个row_id的隐藏列作为主键。(之前索引的创建过程中讲过)
    DB_TRX_ID:每一个事务都会分配一个事务ID,当对某条记录发生变更时,就会将这个事务的事务ID写入trx_id中。
    DB_ROLL_PTR:回滚指针,本质上就是undo log的指针。

当我们执行INSERT时:
begin;
INSERT INTO user (name) VALUES ("tom");

插入的数据都会生成一条insert undo log,并且数据的回滚指针会指向它。undo log会记录undo log的序号、插入主键的列和值…,那么在进行rollback的时候,通过主键直接把对应的数据删除即可。

当我们执行update时:

对于更新的操作会产生update undo log,并且会分更新主键和不更新主键的,假设现在执行:

UPDATE user SET name="Sun" WHERE id=1;

这时会把老的记录写入新的undo log,让回滚指针指向新的undo log,它的undo no是1,并且新的undo log会指向老的undo log (undo no=0)。

假设现在执行:

UPDATE user SET id=2 WHERE id=1;

这下面是把id=1的记录,改为id=2。这里并不是简单的将id为1改为2,而是先将id=1的那个记录删除掉,然后再生成一条记录id=2

对于更新主键的操作,会先把原来的数据deletemark标识打开,这时并没有真正的删除数据,真正的删除会交给清理线程去判断,然后在后面插入一条新的数据,新的数据也会产生undo log,并且undo log的序号会递增。

可以发现每次对数据的变更都会产生一个undo log,当一条记录被变更多次时,那么就会产生多条undo log,undo log记录的是变更前的日志,并且每个undo log的序号是递增的,那么当要回滚的时候,按照序号依次向前推,就可以找到我们的原始数据了。

undo log是如何回滚的

以上面的例子来说,假设执行rollback,那么对应的流程应该是这样:

    通过undo no=3的日志把id=2的数据删除
    通过undo no=2的日志把id=1的数据的deletemark还原成0
    通过undo no=1的日志把id=1的数据的name还原成Tom
    通过undo no=0的日志把id=1的数据删除

2、redo & undo

下面是redo log + undo log的简化过程,便于理解两种日志的过程:

假设有 A B 两个数据,值分别为 1 , 2.
1. 事务开始 ; begin
2. 记录 A = 1 undo log
3. 修改 A = 3
4. 记录 A = 3 redo log
5. 记录 B = 2 undo log
6. 修改 B = 4
7. 记录 B = 4 redo log
8. redo log buffer 写入磁盘 redo log
9. 事务提交

3、CR流程

1. 什么时候会进行 Crash Recovery?
- 实例崩溃之后重启
- innodb_fast_shutdown为1 值关闭实例后重新启动
2. 检测实例是不是干净地关闭的
- 打开 Redo Logs 和系统表空间文件 (ibdataN)
- 读取并从中找到最大的 Checkpoint LSN
- 从最近的 Checkpoint 开始往后扫描 Redo Log
- 如果能够找到 Redo Log 记录,说明还有数据页的更改没有刷新到数据文件上,启动 Crash Re
covery
3.损坏页修复
- 检查双写缓冲区中的所有 128 个页
- 如果页头和页尾的 LSN 不匹配或页面校验和无效,则使用双写缓冲区中的页进行还原
- 如果该页在双写缓冲区中的版本也被破坏,则 server crash
4.前滚 Redo ,提交未提交事务
- 从最近的 Checkpoint 往后扫描到的 Redo Log 记录将被应用到各个数据文件中
- 使用 Undo Log 回滚未提交的 'ACTIVE' 状态的事务         
- 处于 PREPARE 状态的事务,如果打开了 binlog 且在 binlog 有找到对应事务的日志则重新提
交,否则回滚

http://www.mrgr.cn/news/29764.html

相关文章:

  • 【api】java和python联动
  • 【嵌入式】ESP32开发(一)ESP-IDF概述
  • JavaScript总结
  • FFmpeg 4.3 音视频-多路H265监控录放C++开发十三:将AVFrame转换成AVPacket。视频编码原理.编码相关api
  • PHP反序列化靶场(php-SER-libs-main 第一部分)
  • Ubuntu中以root身份运行Qt创建的项目
  • Apache SeaTunnel Zeta引擎源码解析(三) Server端接收任务的执行流程
  • 前后端分离的情况下,后端接口有必要加CSP策略吗?
  • Three.js
  • ChatGPT:强大的人工智能聊天机器人
  • 《深度学习》PyTorch框架 优化器、激活函数讲解
  • C/C++内存分布
  • [数据集][目标检测]智慧养殖场肉鸡目标检测数据集VOC+YOLO格式3548张1类别
  • 41. 如何在MyBatis-Plus中实现批量操作?批量插入和更新的最佳实践是什么?
  • 自监督的主要学习方法
  • C++list的使用:尾插、头插、insert、erase、reverse、sort等的介绍
  • Axure中后台管理信息系统通用原型方案
  • WSL中使用GPU加速AMBER MD--测试
  • Aegisub字幕自动化及函数篇(图文教程附有gif动图展示)(一)
  • 十八,Spring Boot 整合 MyBatis-Plus 的详细配置
  • 麒麟操作系统 xxl-job集群搭建
  • 一家CRO企业终止,业绩成长性遭质疑
  • 字幕编辑用什么软件好?盘点国内外7款视频加字幕软件,简单高效!
  • 传输层协议 —— UDP协议
  • C++: 高效使用智能指针的8个建议
  • vue3中如何拿到vue2中的this