redo log 原理解析

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
日志服务 SLS,月写入数据量 50GB 1个月
简介: redo log 原理解析


redo log 如何保证数据不丢失



在介绍 Buffer Pool 的时候说到,MySQL 的增加改查操作都是在 Buffer Pool 里面进行的,比如更新数据,实际上就是更新 Buffer Pool 里面的缓存页。并且在更新缓存页的时候,还会更新 free 链表、flush 链表、LRU 链表,然后再由专门的后台 IO 线程,不定时地根据 flush 链表、LRU 链表,将更新过的缓存页刷到磁盘文件的数据页里。


但这个机制有一个漏洞,如果一个事务成功提交并更新了缓存页,但 IO 线程还没来得及将缓存页刷到磁盘文件(数据页)里,MySQL 就宕机了。那么很明显,内存数据会丢失,进而造成事务更新的数据丢失。

但是也不可能每提交一次事务,就把事务更新的缓存页往磁盘文件里刷一次,因为缓存页刷新到磁盘文件这个过程是随机写,性能是相当的差,这会导致数据库性能和并发能力大幅减弱。

所以 MySQL 引入了 redo log 机制,这个机制在事务被提交的时候,会将缓存页所做的修改,以日志的形式写入到 redo log 日志文件里面。这种日志大致的格式如下:

在表空间 XX 的第 YY 个数据页中偏移量为 ZZ 的位置更新了数据 DD


redo log 可以保证事务在提交之后数据不丢失,即便 MySQL 在更新之后的缓存页还没刷到磁盘里就宕机了,也是没关系的,因为做的修改已经记录在 redo log 日志里面了。MySQL 重启之后,只需要根据 redo log 重做一遍,恢复出来当时务更新的缓存页,然后再把缓存页刷到磁盘就可以了。

所以 redo log 本质就是保证事务提交成功之后,修改的数据绝对不丢失。而事务提交失败,则会基于 undo log 进行回滚,此时相当于啥也没做。

到这里可能有人会问了,事务提交的时候把修改过的缓存页都刷入磁盘,和事务提交的时候把做的修改都写入 redo log 日志文件,这不都是写磁盘么,有什么区别呢?其实很简单,相信你肯定能想出来。

因为 MySQL 加载数据和刷新数据都是以页为单位的,而缓存页一个是 16kb,数据比较大,刷入磁盘比较耗时。而且我们可能就修改了缓存页里几个字节的数据,如果每次都刷新一个缓存页,那么无疑是浪费资源;而且将缓存页刷入磁盘是随机读写,因为缓存页对应的数据页可能在磁盘任意的一个随机位置,而随机读写性能是非常差的。

但如果是写 redo log 日志则不会有此问题,因为一行 redo log 只占据几十个字节,就包含表空间号、数据页号、磁盘文件偏移量、 更新值,写入磁盘的速度很快。而且这个过程还是顺序 IO,每次都是追加到磁盘文件末尾去,速度是非常快的。

对于机械硬盘而言,虽然随机 IO 的性能很差,但顺序 IO 的性能还是很高的,否则 kafka 就不会有那么高的吞吐量了。

所以提交事务的时候,用 redo log 的形式记录所做的修改,性能会远远超过刷新缓存页的方式,这也可以让数据库的并发能力更强。


初识 redo log


redo log 的作用我们已经知晓,接下来简单看看 redo log 长什么样子。我们说 redo log 包含的内容如下:

在表空间 XX 的第 YY 个数据页中偏移量为 ZZ 的位置更新了数据 DD

所以它需要记录的就是:表空间号+数据页号+偏移量+修改几个字节的值+具体的值

并且根据修改的值的大小,redo log 划分为了不同的类型。比如 MLOG_1BYTE类型的日志指的就是修改了 1 个字节的值,MLOG_2BYTE 类型的日志指的就是修改了 2 个字节的值,以此类推,还有修改了 4 个字节的值的日志类型,修改了 8 个字节的值的日志类型。

当然,如果是一下子修改了一大串的值,类型就是 MLOG_WRITE_STRING,代表一下子在数据页的某个偏移量的位置插入或者修改了一大串的值。

所以一条 redo log 的结构大致如下所示:

redo log 里面记录的就是上面这些东西,因此这条 redo log 表达的语义就很明确了。

  • 日志类型:告诉我们这次增删改操作修改了多少字节的数据;
  • 表空间 ID:在哪个表空间操作的,这个表空间和逻辑概念中的数据表是对应的。此外对于 InnoDB 引擎而言,表空间你可以理解为磁盘上的一个 .ibd 文件,比如我们用 SQL 操作数据表 student ,那么底层就会操作磁盘文件 student.ibd;
  • 数据页号:修改了表空间中的哪些数据页;
  • 数据页中的偏移量:从数据页的哪个位置开始修改的;
  • 具体修改的数据:无需解释了,就是修改之后的数据;


有了这些信息,就可以精准地还原出一次增删改操作造成的数据变动了。

但如果是 MLOG_WRITE_STRING 类型的日志,因为不知道具体修改了多少字节的数据,所以其实会多一个字段,负责指定具体修改了多少字节的数据:

所以这就是 redo log 的底层结构,至少从表面上看并不复杂。当然如果往深了说还是很复杂的比如 redo log 还会记录你更新了哪些索引之类的,这些留到后面再说目前只需要对 redo log 有一个大致的了解即可,当 Buffer Pool 更新完数据之后,就会以上面这种格式往 redo log 日志文件里面写入一条 redo log 日志,来记录本次的修改


redo log 是一条一条写到磁盘里面的吗


我们上面介绍了 redo log 的基本结构,本质上就是一条日志,但它是一条一条写到磁盘里面的吗?答案不是的,其实 MySQL 内部还有一个数据结构,叫做 redo log block。

怎么理解呢?可以类比数据页,一个数据页可以包含很多行数据,而 MySQL 是以页为单位加载和刷新数据的。redo log 也是同理,它也不是单行单行地写入日志文件,而是用一个 redo log block 来存放多个单行日志。

一个 redo log block 是 512 字节,被分为 3 个部分,一个是 12 字节的 header 块头,一个是 496  字节的 body 块体,一个是 4 字节的 trailer 块尾。

而 12 字节的 header 头又分为了 4 个部分。

  • 4 字节的 block no,也就是块的唯一编号;
  • 2 字节的 data length,也就是往块里面写了多少字节的数据;
  • 2 字节的 first record group,这个是说每个事务会有多个 redo log,它们构成了一个组。在这个 block 里的第一组 redo log 的偏移量,就是这 2 个字节存储的;
  • 4 字节的 checkpoint on;

所以从上图可以看出,对于 redo log 而言,它确实是不停地追加写入到 redo log 磁盘文件里的,但其实每一个 redo log 都是写入到文件的一个 redo log block 里,一个 block 最多放 496 条 redo log 日志。

在写 redo log 的时候,先写到 redo log block 的 body 区域里,等满了之后,再将这个 redo log block 写到磁盘中。当然啦,所谓的 redo log block 就是 512 个字节在写文件的时候可以一个字节一个字节地写到磁盘文件,然后文件里面存放的就是很多很多字节,依次排开。然后其中的 512 个字节组合起来,就固定代表了一个 redo log block。

这其实是任何一个中间件系统,数据库系统,底层依赖磁盘文件存储数据的一个共同的原理,所以也不用把 redo log block 写入磁盘文件这个过程想象的太复杂了。就是每次把 512 个字节写到磁盘文件里面去,这 512 个字节我们称之为一个 redo log block。


redo log buffer 是什么



redo log buffer 可以类比 Buffer Pool,它们都是 MySQL 在启动之后就向操作系统申请的一块连续内存空间。Buffer Pool 是申请之后会划分成 N 多个空的缓存页和一些链表结构;redo log buffer 则是申请之后会划分成 N 多个空的 redo log block。

MySQL 提供了 innodb_log_buffer_size 参数用于指定 redo log buffer 的大小,默认是 16MB,已经够大了,毕竟一个 redo log block 才 512 字节而已,而 redo log 基本上也只有几个字节到几十个字节。

所以此时就很清晰了,MySQL 有一个结构叫 redo log buffer,它是 MySQL 在启动之后就向操作系统申请的一块连续内存,并且将内部划分为多个 redo log block。在写 redo log 时,其实是将 redo log 写到 redo log buffer 里的某个 redo log block 里面。并且写的时候,会先从第一个 redo log block 开始写,第一个写满了之后再写下一个,直到所有的 redo log block 都写满。

当 redo log buffer 里所有的 redo log block 都写满之后,那么就要刷到磁盘中了,而这个过程就是将 512 字节的 redo log block 追加到 redo log 日志文件中。

另外有一点需要注意,我们平时在执行事务的时候,每个事务往往会有多个增删改操作,那么就会有多个 redo log,这多个 redo log 就是一组 redo log。然后每次一组 redo log 都是先在别的地方暂存,等到执行完了,再把这一组 redo log 写到 redo log buffer 的 block 里去。

如果一组 redo log 太大了,比如更新的数据非常多,那么有可能造成一个 redo log block 放不下,此时就会存放在两个或多个 redo log block 中。但很多时候 redo log 都是不大的,那么一个 redo log block 就存放多组 redo log。

所以总结一下:一个事务往往会产生多条 redo log,它们形成了一组 redo log,然后一组 redo log 在事务提交时会被顺序写入到 redo log buffer 的某个 redo log block 中。


redo log buffer 何时刷盘


这里来探讨一个新的问题,redo log buffer 何时刷盘呢?直接给出结果:

1)如果写入 redo log buffer 的日志已经占据了总容量(16MB)的一半了,也就是有超过 8MB 的 redo log 在缓冲里,此时就会把它们刷入到磁盘文件里;

2)一个事务提交的时候,必须把它的那些 redo log 所在的 redo log block 都刷入到磁盘文件里去。只有这样,当事务提交之后,修改的数据才不会丢失,因为 redo log 里有重做日志,随时可以恢复事务做的修改;

3)后台线程定时刷新,有一个后台线程每隔 1 秒就会把 redo log buffer 里的 redo log block 刷到磁盘文件里去;

4)MySQL 关闭的时候,redo log block 都会刷入到磁盘里去;

忽略上面的第四条,因为关闭 MySQL 的时候必然会刷 redo log 到磁盘,我们看前三条。可以肯定的是,如果你瞬间执行了大量高并发的 SQL 语句,1 秒内就产生了超过 8MB 的 redo log,此时占据了 redo log buffer 一半的空间了,那么必然会直接把你的 redo log 刷入磁盘里去。

此外,平常执行的简单事务一般都是在几十毫秒到几百毫秒之间完成,单机事务性能一般不会超过 1 秒,否则数据库就太慢了。而如果在几十毫秒内执行完了一个事务,此时也会立马把这个事务的 redo log 都刷入磁盘。

总之,要保证执行事务的时候,redo log 都进入 redo log buffer,提交事务的时候,redo log 必须刷入磁盘文件,接着才算是事务提交成功,否则事务提交就是失败。保证这一点,就能确保事务提交之后,数据不会丢,因为有 redo log 在磁盘里面。

当然,要保证数据绝对不丢,还得将配置参数 innodb_flush_log_at_trx_commit 设置为 1(默认值),表示提交事务时,强行将 redo log 从 redo log buffer 刷到磁盘文件里面。

还是很好理解的,但这里还有一个问题,我们知道每一次增删改,MySQL 都会产生 redo log,这些 redo log 最终落入磁盘文件中,而该文件我们称为 redo log 文件。但是问题来了,redo log 文件只会有一个吗?

首先 redo log 文件会有一个专门的目录,这个目录可以通过 show variables like 'datadir' 来查看,通过 innodb_log_group_home_dir 参数进行设置。

然后该目录下的 redo log 文件可以有多个,每个文件的大小通过 innodb_log_file_size 参数指定,默认是 48MB,写满了一个就会写下一个。而数量则通过 innodb_log_files_in_group 参数指定,默认是 2 个。

所以默认情况下,目录里就两个日志文件,分别为 ib_logfile0 和 ib_logfile1,每个 48MB。先写第一个,写满了再写第二个,但如果第二个也写满了呢?很简单,继续写第一个,覆盖第一个日志文件里原来的 redo log 就可以了。

所以 MySQL 默认保留了最近的 96MB 的 redo log,但这其实已经很多了,毕竟 redo log 很小,一条通常就几个字节到几十个字节不等,96MB 足够存储上百万条 redo log 了。当然,如果你还想保留更多的 redo log,那么调节上述两个参数就可以了,比如将每个 redo log 文件的大小指定为 96MB,最多保留 100 个 redo log文件,这样就能存储最近 9 GB 多的 redo log。



本文深度参考自:

  • 儒猿技术窝《MySQL 实战高手》
相关文章
|
17天前
|
存储 缓存 关系型数据库
图解MySQL【日志】——Redo Log
Redo Log(重做日志)是数据库中用于记录数据页修改的物理日志,确保事务的持久性和一致性。其主要作用包括崩溃恢复、提高性能和保证事务一致性。Redo Log 通过先写日志的方式,在内存中缓存修改操作,并在适当时候刷入磁盘,减少随机写入带来的性能损耗。WAL(Write-Ahead Logging)技术的核心思想是先将修改操作记录到日志文件中,再择机写入磁盘,从而实现高效且安全的数据持久化。Redo Log 的持久化过程涉及 Redo Log Buffer 和不同刷盘时机的控制参数(如 `innodb_flush_log_at_trx_commit`),以平衡性能与数据安全性。
27 5
图解MySQL【日志】——Redo Log
|
14天前
|
安全 算法 网络协议
解析:HTTPS通过SSL/TLS证书加密的原理与逻辑
HTTPS通过SSL/TLS证书加密,结合对称与非对称加密及数字证书验证实现安全通信。首先,服务器发送含公钥的数字证书,客户端验证其合法性后生成随机数并用公钥加密发送给服务器,双方据此生成相同的对称密钥。后续通信使用对称加密确保高效性和安全性。同时,数字证书验证服务器身份,防止中间人攻击;哈希算法和数字签名确保数据完整性,防止篡改。整个流程保障了身份认证、数据加密和完整性保护。
|
6天前
|
机器学习/深度学习 数据可视化 PyTorch
深入解析图神经网络注意力机制:数学原理与可视化实现
本文深入解析了图神经网络(GNNs)中自注意力机制的内部运作原理,通过可视化和数学推导揭示其工作机制。文章采用“位置-转移图”概念框架,并使用NumPy实现代码示例,逐步拆解自注意力层的计算过程。文中详细展示了从节点特征矩阵、邻接矩阵到生成注意力权重的具体步骤,并通过四个类(GAL1至GAL4)模拟了整个计算流程。最终,结合实际PyTorch Geometric库中的代码,对比分析了核心逻辑,为理解GNN自注意力机制提供了清晰的学习路径。
143 7
深入解析图神经网络注意力机制:数学原理与可视化实现
|
7天前
|
机器学习/深度学习 缓存 自然语言处理
深入解析Tiktokenizer:大语言模型中核心分词技术的原理与架构
Tiktokenizer 是一款现代分词工具,旨在高效、智能地将文本转换为机器可处理的离散单元(token)。它不仅超越了传统的空格分割和正则表达式匹配方法,还结合了上下文感知能力,适应复杂语言结构。Tiktokenizer 的核心特性包括自适应 token 分割、高效编码能力和出色的可扩展性,使其适用于从聊天机器人到大规模文本分析等多种应用场景。通过模块化设计,Tiktokenizer 确保了代码的可重用性和维护性,并在分词精度、处理效率和灵活性方面表现出色。此外,它支持多语言处理、表情符号识别和领域特定文本处理,能够应对各种复杂的文本输入需求。
38 6
深入解析Tiktokenizer:大语言模型中核心分词技术的原理与架构
|
1月前
|
存储 SQL 关系型数据库
MySQL日志详解——日志分类、二进制日志bin log、回滚日志undo log、重做日志redo log
MySQL日志详解——日志分类、二进制日志bin log、回滚日志undo log、重做日志redo log、原理、写入过程;binlog与redolog区别、update语句的执行流程、两阶段提交、主从复制、三种日志的使用场景;查询日志、慢查询日志、错误日志等其他几类日志
105 35
MySQL日志详解——日志分类、二进制日志bin log、回滚日志undo log、重做日志redo log
|
1月前
|
机器学习/深度学习 算法 数据挖掘
解析静态代理IP改善游戏体验的原理
静态代理IP通过提高网络稳定性和降低延迟,优化游戏体验。具体表现在加快游戏网络速度、实时玩家数据分析、优化游戏设计、简化更新流程、维护网络稳定性、提高连接可靠性、支持地区特性及提升访问速度等方面,确保更流畅、高效的游戏体验。
75 22
解析静态代理IP改善游戏体验的原理
|
26天前
|
编解码 缓存 Prometheus
「ximagine」业余爱好者的非专业显示器测试流程规范,同时也是本账号输出内容的数据来源!如何测试显示器?荒岛整理总结出多种测试方法和注意事项,以及粗浅的原理解析!
本期内容为「ximagine」频道《显示器测试流程》的规范及标准,我们主要使用Calman、DisplayCAL、i1Profiler等软件及CA410、Spyder X、i1Pro 2等设备,是我们目前制作内容数据的重要来源,我们深知所做的仍是比较表面的活儿,和工程师、科研人员相比有着不小的差距,测试并不复杂,但是相当繁琐,收集整理测试无不花费大量时间精力,内容不完善或者有错误的地方,希望大佬指出我们好改进!
91 16
「ximagine」业余爱好者的非专业显示器测试流程规范,同时也是本账号输出内容的数据来源!如何测试显示器?荒岛整理总结出多种测试方法和注意事项,以及粗浅的原理解析!
|
17天前
|
Java 数据库 开发者
详细介绍SpringBoot启动流程及配置类解析原理
通过对 Spring Boot 启动流程及配置类解析原理的深入分析,我们可以看到 Spring Boot 在启动时的灵活性和可扩展性。理解这些机制不仅有助于开发者更好地使用 Spring Boot 进行应用开发,还能够在面对问题时,迅速定位和解决问题。希望本文能为您在 Spring Boot 开发过程中提供有效的指导和帮助。
63 12
|
8天前
|
人工智能 运维 监控
一招高效解析 Access Log,轻松应对泼天流量
一招高效解析 Access Log,轻松应对泼天流量
|
1月前
|
SQL 缓存 关系型数据库
MySQL原理简介—7.redo日志的底层原理
本文介绍了MySQL中redo日志和undo日志的主要内容: 1. redo日志的意义:确保事务提交后数据不丢失,通过记录修改操作并在系统宕机后重做日志恢复数据。 2. redo日志文件构成:记录表空间号、数据页号、偏移量及修改内容。 3. redo日志写入机制:redo日志先写入Redo Log Buffer,再批量刷入磁盘文件,减少随机写以提高性能。 4. Redo Log Buffer解析:描述Redo Log Buffer的内存结构及刷盘时机,如事务提交、Buffer过半或后台线程定时刷新。 5. undo日志原理:用于事务回滚,记录插入、删除和更新前的数据状态,确保事务可完整回滚。
119 22

推荐镜像

更多