MySQL的日志--Redo Log【学习笔记】

news2025/4/24 12:58:33

MySQL的日志--Redo Log

知识来源:

  • 《MySQL是怎样运行的》--- 小孩子4919

MySQL的事务四大特性之一就是持久性(Durability)。但是底层是如何实现的呢?这就需要我们的Redo Log(重做日志)闪亮登场了。它记录着事务的所有操作,等到MySQL崩溃之后,哪怕修改的脏页生前还在Buffer Pool中遨游,此时也可以读取Redo Log中的信息,将这些脏页生前的样貌刻在磁盘上。

有关于Buffer Pool的知识可以查看mysql的Buffer Pool【学习笔记】-CSDN博客。

为什么持久化需要Redo Log?

最直接的方法,可以是等事务执行完成之后,立刻进行持久化操作。那为什么还需要Redo Log?

  • 直接持久化的弊端

    • 如果事务执行完成之后直接持久化,那么可能会进行大量的随机IO刷盘脏页数据。这时候,性能损耗很大,用户会觉得MySQL慢慢的。

    • 并且直接持久化的空窗期较大,如果在这个时候断电,那么可能会出现事务部分操作成功,部分失败的情况。

  • Redo Log的好处

    • Redo Log记录的是MySQL执行语句需要的必要信息,比如:插入一条记录,那么Redo Log日志就会记录字段数量,插入页面的偏移量等等信息。相较于刷脏页的16KB,这短短的几十上百字节显得微不足道。并且Redo Log是顺序IO,也就是怼着Redo Log文件追加记录。持久化的空窗期就很小,持久化的效率很高!丝毫不影响用户体验。

Redo Log的格式

Redo Log的通用格式

根据type的不同,对应的data内部的格式和内容也会不同。这个通用格式比作一辆车,那么type就是车的型号,型号不同,那车的样子也会不同。

  • type:Redo Log的类型,MySQL5.7.22中共有53种不同的类型。

  • space ID:表空间ID。不知道表空间的可以看InnoDB的表空间【学习笔记】-CSDN博客。

  • page number:页号。也在表空间的文章中有说明。

  • data:根据type的不同而不同。

简单的Redo Log

如果该Redo Log中记录的是修改某页中的某个字段。如:修改系统表空间页号为7的页面中Max Row ID属性。MySQL在启动时,会将这个属性读取到内存赋值给一个全局变量中,用于赋值给那些有隐藏列row_id的新插入记录,随机自增1。等到这个值为256的倍数时,就会将该值持久化。此时会先在Buffer Pool中该页对应的缓冲页中修改该值,然后再记录Redo Log。等下次系统启动时,会读取Max Row ID属性增加256后(可能重启前,该值为257,需要保证这个值大于重启前的值),再赋值给内存中的全局变量。

  • 对于这种只要将修改页面中某个偏移量开始的几个字节的值持久化的情况。只需要很简单的Redo Log便可以,这种Redo Log被称为物理日志。具体类型如下:

    • MLOG_1BYTE(type字段值为1):表示页面某个偏移量处写入1个字节。

    • MLOG_2BYTE(type字段值为2):表示页面某个偏移量处写入2个字节。

    • MLOG_4BYTE(type字段值为4):表示页面某个偏移量处写入4个字节。

    • MLOG_8BYTE(type字段值为8):表示页面某个偏移量处写入8个字节。

    • MLOG_WRITE_STRING(type字段值为30):表示页面某个偏移量处写入1个字节序列。

复杂的Redo Log格式

对于修改一个页面的一个字段来说,简单的Redo Log已经足够了。但是如果你需要插入一条记录(如果页面剩余空间不够,可能还需要进行页分裂)。并且被修改页面中的Page Header、Page Directory中都有值需要修改。例如:Page Directoy中的槽信息,Page Header中的PAGE_N_DIR_SLOTS(槽数量)。。

这时候简单的Redo Log格式已经不够用了。需要更加抽象,上层的记录方式,比如记录插入语句的必要信息,等数据恢复的时候,执行插入语句,而不是将对应修改的字段更新。

  • 这些对应的type分别是:

    • MLOG_REC_INSERT(type的值为9):表示插入一条非紧凑行格式(REDUNDANT)

    • MLOG_COMP_REC_INSERT(type的值为38):表示插入一条紧凑行格式记录(COMPACT、DYNAMIC、COMPRESSED)

    • MLOG_COMP_REC_DELETE(type的值为58):表示删除一条紧凑行格式记录(COMPACT、DYNAMIC、COMPRESSED)

    • MLOG_COMP_LIST_START_DELETE(type的值为44):表示从给定的某条记录开始删除一系列紧凑型行格式记录

    • MLOG_COMP_LIST_END_DELETE(type的值为43):与MLOG_COMP_LIST_START_DELETE相呼应,表示删除一系列紧凑型行格式记录,直到MLOG_COMP_LIST_END_DELETE记录的行格式为止

  • 这些Redo Log的data中的内容都是逻辑语句,而不是物理层面具体的偏移量处的值。

  • 这边以MLOG_COMP_REC_INSERT举例子。

    • n_uniques:表示需要n_uniques个字段才能保证记录唯一。比如:对于聚簇索引来说,就是主键列数,对于二级索引来说,就是索引列+主键列数(无论记录对应的索引列是否为NULL)。

    • field1_len~fieldn_len:若干个字段存储空间大小。无论固定长度类型(INT)还是可变长度类型(VARCHAR)。

    • offset:前一条记录在页面中的偏移量,用于恢复时更新该记录记录头中的next_record属性为新插入记录真实数据开始的偏移量。

    • end_seg_len:可以转换成该记录占用存储空间的总大小。具体转换方式不展开。

Mini-Transaction(MTR)

  • 首先提出一个最重要的问题--有了Transaction为什么还需要Mini-Transaction

    • Tansaction包含一系列数据库操作,每个操作对应一条数据库语句。

    • 而每条数据库语句底层MySQL会执行很多动作分别是一条redo log,比如:向满的页面插入一条记录,MySQL就会执行页分裂:开辟新页、根据主键平分原来的数据(不考虑90-10划分机制)、插入新数据、新增目录项。(当然还有更新二级索引、系统页什么的。。这边就省略了)

    • 这个时候如果中途崩溃了,也就是说,只记录到"新数据插入"这条redo log,后面的日志没了。等到时候开始恢复数据,MySQL执行这一系列记载下来的redo log,但是目录项没有生成,那这个B+树不就出问题了?

    • 所以这时候需要将对底层页面的操作划分成一个一个组,每个组被称为MTR,保证原子性,一组的redo log要不全部记录,要不全部不记录。如:对聚簇索引的操作划分成一个不可分割的组,这个组内的操作需要保证原子性。

  • 如何划分

    • 如果一个语句生成多个redo log

      • 那么在这一系列redo log后面再加上type为MLOG_MULTI_REC_ENDredo log(里面有且仅有type这个字段),在解析redo log的时候,如果没有读到这个类型的redo log,那么就会将之前解析的redo log全部丢弃,一句也不执行。

    • 如果一个语句生成一个redo log

      • 当然按照上面的方法也可以,但是有更好的方法节省一个字节。上面提到redo log最多也就53中类型,那么type的第一位就可以拿出来标记为1,代表这个需要保证原子性的操作只产生了一条单一的redo log。(其实上面也可以采用这种方法,比如10001,那轻易可以划分成1|0001,但是可能考虑兼容性吧。。。)

  • Mini-Transaction在事务中的层次

    • 当然不同事务是可以并发持久化的,并且持久化单元就是MTR。

Redo Log写入过程

存储结构---Redo Log Block
  • 每个Redo Log Block占512字节。有点类似于表空间中的页,有头有尾,中间部分存放记录。一条Redo Log可以被称为Redo Log记录。

    • log block header

      • LOG_BLOCK_HDR_NO:每个block都有编号。就像页面有页号。

        • 计算公式是((lsn/512)&0x3FFFFFFF)+1,这个lsn后面会讲到。

          • 此时可以看到LOG_BLOCK_HDR_NO最大为1G(2^30),最多存在1G个block,所以Redo Log File最多也就512GB

          • LOG_BLOCK_HDR_NO第一位是flush bit,该值为1,则表示本block是在将Redo Log Buffer中的block落盘操作中,第一个被刷入的block

      • LOG_BLOCK_HDR_DATA_LEN:表示block使用的字节数。初始值为12字节,全部使用完则为512字节,否则则为redo log大小+12字节。

      • LOG_BLOCK_FIRST_REC_GROUP:表示该block中第一个MTR中的第一个redo log记录偏移量。

      • LOG_BLOCK_CHECKPOINT_NO:表示checkpoint的序号。

    • log block trailer

      • LOG_BLOCK_CHECKSUM:校验和。

内存中---Redo Log Buffer

类似于缓冲页的Buffer Pool,块也有缓冲,叫做Redo Log Buffer,简称log buffer。在MySQl启动的时候向操作系统申请的连续内存空间,可以通过启动选项innodb_log_buffer_size来指定大小,默认16MB。

  • log buffer中写入redo log记录是顺序写入的,并且通过全局变量buf_free指针来标记后续redo log记录该从哪里开始写。

  • buf_next_to_write指针标记后续redo log记录从哪里开始刷入磁盘。

  • redo log写入是以MTR为单位的,并且不同事务的MTR可以交替写入。比如:

    • 事务T1的两个MTR分别称为mtr_t1_1和mtr_t1_2;

    • 事务T2的两个MTR分别称为mtr_t2_1和mtr_t2_2;

磁盘中---Redo Log File
  • Redo Log Buffer刷盘到Redo Log File的时机

    • Redo Log Buffer使用容量超过50%左右,就开始将缓存落盘。

    • 默认情况下,事务提交时会立即将缓存落盘。可以通过innodb_flush_log_at_trx_commit系统变量设置,默认值为1。

      • 0:事务提交后,不立即将缓存落盘。缓存落盘的任务交给了后台线程。

      • 1:事务提交后,立即将缓存落盘。(其实是先写到操作系统page cache中,然后调用fsync()系统调用,立即将page cache中的数据写到磁盘中)

      • 2:事务提交后,立即将缓存写到操作系统page cache中,具体何时写到磁盘,完全根据操作系统安排。

    • 后台线程每秒一次,将缓存落盘。

    • 正常关闭服务器

    • 做checkpoint

  • Redo Log File默认存放在MySQL的数据目录中,可以通过innodb_log_group_home_dir设置。默认有两个文件--ib_logfile1ib_logfile2,可以通过innodb_log_files_in_group设置数量,文件的命名采用ib_logfile[n],n=0,1,2...。每个文件大小默认48MB,可以通过innodb_log_file_size设置。

    • 书写顺序是从ib_logfile0开始写,写满则写ib_logfile1,不断延续。如果全部都被写满了,则从ib_logfile0重新开始写。

    • Redo Log File的总大小是innodb_log_files_in_group * innodb_log_file_size

  • Redo Log File的格式

    • 由两部分组成前四个block+后续存储Redo Log的block

      • 前四个block的结构

        • log file header:描述该redo log file的整体信息

        • checkpoint1

        • checkpoint2:和checkpoint1一致

Log Sequence Number(LSN)

  • lsnMySQL的一个全局遍历,用来标注系统中写入的Redo Log量(包括刷入磁盘的、和存在Redo Log Buffer中的)

    • 初始值为8704,后续增加多少Redo Log量,则增加相应的值(还包括block的头尾所占的字节)

      • 此时mtr_1为200字节,mtr_2为1000字节,那么计算当前lsn则为8704+200+1000+12*3+4*2=9948字节

  • 而还有一个全局变量flushed_to_disk_lsn,则用来标注系统刷入磁盘的Redo Log量。这个变量的计算方式同lsn

  • flush链表(存在于Buffer Pool,专门用于记录脏页的控制块)中的lsn

    • 控制块中有两个变量oldest_modificationnewest_modificationoldest_modification记录第一次修改该缓冲页前MTR所对应的lsn(只要该脏页的控制块存在于链表中,那么这个变量就只会赋值一次,并且按照这个值从大到小排序);newest_modification记录最近一次修改脏页后MTRlsn值。举个例子:

      • 原来的链表样子

      • MTR3修改了页b和页d,修改前的lsn为9948,修改后的lsn为10000

checkpoint

如果脏页落盘,那么记录修改对应脏页的Redo Log也就没有存在的必要了,可以被后面新的Redo Log覆盖了,checkpoint这个机制便是判断redo log file是否可以覆盖的保证。串联了flush中的lsnRedo Log File前四个block中LOG_HEADER_START_LSN和LOG_CHECKPOINT_LSN这两个属性。请看下面的例子:

  • 现在MySQL中Buffer PoolRedo Log BufferRedo Log File的样子

    • 在执行了mtr_1mtr_2mtr_3后的样子,并且除了mtr_3,其余mtr都完成了持久化

  • 随后mtr_1对应的脏页a完成了持久化,那么此时mtr_1中的Redo Log都失去了作用。

    • 此时会将该脏页的newest_modification赋值给磁盘中对应页File HeaderFILE_PAGE_LSN字段(后续用于快速恢复,不知道页结构的可以看mysql如何将数据组织起来的,索引(自底向上,以innodb存储引擎为例)【学习笔记】-CSDN博客)

  • 异步线程就会执行一次checkpoint,来设置Redo Log File头部的那两个属性。(脏页落盘和执行checkpoint虽然是有明显的先后顺序,但并不是一次脏页落盘,就执行一次checkpoint

    • 内存中有一个全局变量checkpoint_lsn来记录当前可以覆盖的Redo Log总量大小,初始化为8704

    • 将当前MySQL中最早修改的脏页对应的的控制块中的oldest_modification赋值给checkpoint_lsn,由于脏页a已经落盘,那么最早修改的脏页就成为了脏页c,为8916。

    • 内存中有一个全局变量checkpoint_no来记录当前checkpoint的编号。每次执行完checkpoint后会赋值给LOG_CHECKPOINT_NO,随后自增1。

      • checkpoint_no的值是偶数时,就写到checkpoint1中;是奇数时,就写checkpoint2中。

    • checkpoint_lsn赋值给LOG_CHECKPOINT_LSN并通过checkpoint_lsn计算出对应Redo Log File中的offset赋值给LOG_CHECKPOINT_OFFSET

    • checkpoint完成后,各个lsn的值

      • 可以通过show engine innodb status\G查看各个lsn

崩溃恢复

  • 确定起点:首先通过比较两个checkpoint blockLOG_CHECKPOINT_LSN,选出较大的checkpoint block。并且通过其LOG_CHECKPOINT_OFFSET获得到Redo Log File对应的偏移量,这个便是恢复的起点。(从前面可知,lsn小于LOG_CHECKPOINT_LSNRedo Log所对应的脏页已经成功落盘了。至于大于等于lsnRedo Log记录无法确定是否落盘,因为脏页落盘和checkpoint是异步的,在两者间的空窗期,发生崩溃的话,就无法通过LOG_CHECKPOINT_LSN确定哪些脏页已经落盘了)

  • 确定终点:从起点开始,一路向后遍历,直到遍历到log block headerLOG_BLOCK_HDR_DATA_LEN不为512字节的(如果为512字节,则表示该block全部写满Redo Log记录)

  • 快速恢复手段:

    • 使用hash结构:将需要恢复的Redo Log记录中的表空间ID和页号作为key,Redo Log记录作为value存入hash结构,对于修改同一页面的Redo Log用链表串起来,一起执行。

    • 使用磁盘页中File Header中的FILE_PAGE_LSN字段:前面提到这个字段记录脏页的控制块中newest_modification的值,也就是说如果这个值大于LOG_CHECKPOINT_LSN,那么该脏页一定是在执行最后一次checkpoint后落盘的,那么该页相关的lsn小于FILE_PAGE_LSNRedo Log就不需要被用于恢复了(也就是直接掠过这些Redo Log记录)

TIPS:此时有一个巨大的问题,也就是说,如果有一个事务执行到一半,并且前半个事务的sql语句都被记录到Redo Log中并且持久化了(innodb_flush_log_at_trx_commit设置为0,并且这个事务执行时间超过1秒)。这个时候恢复不就不能保证该事务的原子性了吗。非也,这个时候Undo Log熠熠生辉。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2341493.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【AI应用】免费代码仓构建定制版本的ComfyUI应用镜像

免费代码仓构建定制版本的ComfyUI应用镜像 1 创建代码仓1.1 注册登陆1.2 创建代码仓1.5 安装中文语言包1.4 拉取ComfyUI官方代码2 配置参数和预装插件2.1 保留插件和模型的版本控制2.2 克隆插件到代码仓2.2.1 下载插件2.2.2 把插件设置本仓库的子模块管理3 定制Docker镜像3.1 创…

​​Agentic AI——当AI学会主动思考与决策,世界将如何被重塑?

一、引言:2025,Agentic AI的元年 “如果ChatGPT是AI的‘聊天时代’,那么2025年将开启AI的‘行动时代’。”——Global X Insights[1] 随着Agentic AI(自主决策型人工智能)的崛起,AI系统正从被动应答的“工具…

Ollama API 应用指南

1. 基础信息 默认地址: http://localhost:11434/api数据格式: application/json支持方法: POST(主要)、GET(部分接口) 2. 模型管理 API (1) 列出本地模型 端点: GET /api/tags功能: 获取已下载的模型列表。示例:curl http://lo…

PNG透明免抠设计素材大全26000+

在当今的数字设计领域,寻找高质量且易于使用的素材是每个设计师的日常需求。今天,我们将为大家介绍一个超全面的PNG透明免抠设计素材大全,涵盖多种风格、主题和应用场景,无论是平面设计、网页设计还是多媒体制作,都能轻…

4.多表查询

SQL 多表查询:数据整合与分析的强大工具 文章目录 SQL 多表查询:数据整合与分析的强大工具一、 多表查询概述1.1 为什么需要多表查询1.2 多表查询的基本原理 二、 多表查询关系2.1 一对一关系(One-to-One)示例: 2.2 一…

美团2024年春招第一场笔试 C++

目录 1&#xff0c;小美的平衡矩阵 2&#xff0c;小美的数组询问 3&#xff0c;小美的MT 4&#xff0c;小美的朋友关系 1&#xff0c;小美的平衡矩阵 【题目描述】 给定一个n*n的矩阵&#xff0c;该矩阵只包含数字0和1。对于 每个i(1<i<n)&#xff0c;求在该矩阵中&am…

XHTMLConverter把docx转换html报java.lang.NullPointerException异常

一.报错 1.报错信息 org.apache.poi.xwpf.converter.core.XWPFConverterException: java.lang.NullPointerExceptionat org.apache.poi.xwpf.converter.xhtml.XHTMLConverter.convert(XHTMLConverter.java:77)at org.apache.poi.xwpf.converter.xhtml.XHTMLConverter.doConve…

OpenCV 图形API(52)颜色空间转换-----将 NV12 格式的图像数据转换为 RGB 格式的图像

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 将图像从 NV12 (YUV420p) 色彩空间转换为 RGB。该函数将输入图像从 NV12 色彩空间转换到 RGB。Y、U 和 V 通道值的常规范围是 0 到 255。 输出图…

COdeTop-206-反转链表

题目 206. 反转链表 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[5,4,3,2,1]示例 2&#xff1a; 输入&#xff1a;head [1,2] 输出&#xff1a;[2,1]示例 …

线段树讲解(小进阶)

目录 前言 一、线段树知识回顾 线段树区间加减 区间修改维护&#xff1a; 区间修改的操作&#xff1a; 区间修改update&#xff1a; 线段树的区间查询 区间查询&#xff1a; 区间查询的操作&#xff1a; 递归查询过程&#xff1a; 区间查询query&#xff1a; 代码&…

openharmony5.0.0中C++公共基础类测试-线程相关(一)

C公共基础类测试及源码剖析 延续传统&#xff0c;show me the code&#xff0c;除了给出应用示例还重点分析了下openharmony中的实现。 简介 openharmony中提供了C公共基础类库&#xff0c;为标准系统提供了一些常用的C开发工具类&#xff0c;本文分析其实现&#xff0c;并给…

TDengine 数据订阅设计

简介 数据订阅作为 TDengine 的一个核心功能&#xff0c;为用户提供了灵活获取所需数据的能力。通过深入了解其内部原理&#xff0c;用户可以更加有效地利用这一功能&#xff0c;满足各种实时数据处理和监控需求。 基本概念 主题 与 Kafka 一样&#xff0c;使用 TDengine 数…

URP-UGUI交互功能实现

一、非代码层面实现交互&#xff08;SetActive&#xff09; Button &#xff1a;在OnClick&#xff08;&#xff09;中添加SetActive方法&#xff08;但是此时只首次有效&#xff09; Toggle &#xff1a;在OnClick&#xff08;&#xff09;中添加动态的SetActive方法 &#…

UniGoal 具身导航 | 通用零样本目标导航 CVPR 2025

UniGoal的提出了一个通用的零样本目标导航框架&#xff0c;能够统一处理多种类型的导航任务 &#xff08;如对象类别导航、实例图像目标导航和文本目标导航&#xff09;&#xff0c;而无需针对特定任务进行训练或微调。 它的特点是 图匹配与多阶段探索策略&#xff01;&#x…

通过Quartus II实现Nios II编程

目录 一、认识Nios II二、使用Quartus II 18.0Lite搭建Nios II硬件部分三、软件部分四、运行项目 一、认识Nios II Nios II软核处理器简介 Nios II是Altera公司推出的一款32位RISC嵌入式处理器&#xff0c;专门设计用于在FPGA上运行。作为软核处理器&#xff0c;Nios II可以通…

Linux/AndroidOS中进程间的通信线程间的同步 - IPC方式简介

前言 从来没有总结过Linux/Android系统中进程间的通信方式和线程间的同步方式&#xff0c;这个专栏就系统总结讨论一下。首先从标题可知&#xff0c;讨论问题的主体是进程和线程、通信和同步&#xff1b;在这里默认你理解进程和线程的区别。通信和同步有什么概念上的区别&…

Windows:注册表配置应用

0、简介 本篇博客记录一下&#xff0c;日常的系统注册表配置选项&#xff0c;以防再次遇到问题不知如何解决。 1、开机启动配置 HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run :: 此位置存储了所有用户登录时需要启动的程序。 在该项下新建字符串值&#…

WebXR教学 05 项目3 太空飞船小游戏

准备工作 自动创建 package.json 文件 npm init -y 安装Three.js 3D 图形库&#xff0c;安装现代前端构建工具Vite&#xff08;用于开发/打包&#xff09; npm install three vite 启动 Vite 开发服务器&#xff08;推荐&#xff09;&#xff08;正式项目开发&#xff09; …

达梦统计信息收集情况检查

查询达梦某个对象上是否有统计信息 select id,T_TOTAL,N_SMAPLE,N_DISTINCT,N_NULL,BLEVEL,N_LEAF_PAGES,N_LEAF_USED_PAGES,LAST_GATHERED from sysstats where id IN (select id from sysobjects where upper(name)upper(&objname));可能有系统对象&#xff0c;可以增加…

【matlab】气泡图的应用

【matlab】气泡图的应用 .rtcContent { padding: 30px; } .lineNode {font-size: 12pt; font-family: "Times New Roman", Menlo, Monaco, Consolas, "Courier New", monospace; font-style: normal; font-weight: normal; } clear load zb_equi.mat load …