MySQL三大日志【新手快速入门 一文助你理解redo log刷盘和数据页刷盘的区别】

MySQL三大日志(redo log,binlog,undo log)

在文章开始前先抛出一些概念和问题,对已经接触过三大日志的读者可以做一个自测,如果不会那么本文将会详解这些概念助你理解,如果都会,那你可以忽略本文了(开个玩笑,可以看看查漏补缺)

常见问题:
三大日志的作用是什么?
Redo Log的三种刷盘策略(0、1、2)有何区别?
MySQL挂机和系统宕机有何区别?为什么有的场景数据会丢失?
Redo Log事务执行到一半被后台线程强制刷盘怎么办?
Redo Log刷盘和数据页(脏页)刷盘的区别是什么?
WAL是什么?为什么需要先写日志再修改数据页,而不是直接刷盘数据页?

先从三大日志的作用与功能入手:

  • redo log(重做日志):redo log(重做日志)是 InnoDB 存储引擎独有的,它让 MySQL 拥有了崩溃恢复能力;
    MySQL 实例挂了或宕机了,重启时,InnoDB 存储引擎会使用.idb文件 和redo log恢复数据(.ibd文件是InnoDB存储引擎层的物理存储文件,也就是存储实际数据页的地方)
    redo log 它是物理日志,记录内容是“在某个数据页上做了什么修改”

(下图是redo log的流程)
在这里插入图片描述

流程解析:当查询数据时,若Buffer Pool中没有,就从磁盘中找,并且放入Buffer Pool,后续的查询和更新都在Buffer Pool中,然后会把“在某个数据页上做了什么修改”记录到重做日志缓存(redo log buffer),最后定期刷盘到redo log文件中

可以看到步骤2 3都是随着事务进行而进行的,那么什么时候执行第四步刷盘呢?
redo log刷盘有多种情况(比如事务提交,log buffer 空间不足,Checkpoint检查等情况,但是为了降低理解难度,我们先只讲最主要的事务提交的刷盘策略)
我们主要学习的就是事务提交时的刷盘策略,由参数innodb_flush_log_at_trx_commit控制,共有三种刷盘策略,对应参数的值为 0,1,2 我们按照优先级(持久化能力从低到高,性能从高到低)来讲:

提前对下面的图提前做两个常识性的补充:
一. 首先明确我们的redo log写入流程还有个BufferPool,但是下面三张图省略了,我们只需要知道当我们执行语句时Buffer Poolredo log buffer是同步进行的即可;
二. 其次我们把MySQL后台线程的执行流程分成两块,把redo log buffer写入page cache称为write,从page cache刷盘到磁盘称为fsync,这两个函数熟悉操作系统的都应该知道,我们对不熟悉的读者进行一个补充

0:
在这里插入图片描述

我们执行事务,正常在Buffer Poolredo log buffer中更新数据和记录 (这个流程三种策略都是一样的,后面不赘述)
然后MySQL的后台线程每秒都会调用writefsync,把redo log buffer中的内容先写入page cache,再刷盘到磁盘
这种情况下不论是 MySQL挂了 还是 设备宕机都可能丢失1秒的数据

常识性补充:
MySQL实例挂机会导致redo log buffer中数据丢失;
Page Cache是操作系统的内核缓冲区,属于操作系统层面,系统宕机和断电均会导致Page Cache中数据丢失

2:
在这里插入图片描述

这里相比0的情况,区别就是每次提交事务的时候会额外进行一个write操作,把redo log buffer写到page cache,其他操作一样
这种情况下 MySQL挂了不丢失数据 还是 设备宕机可能丢失1秒的数据

1:
在这里插入图片描述

这里相比0的情况,区别就是每次提交事务的时候会额外进行writefsync,直接把redo log buffer写入page cache并且刷盘
相比前两种情况,这里的后台线程每秒扫描的时候,如果正好提交事务,刚刷过盘,那么后台线程是不会进行刷盘的,即后台线程的 fsync() 仅在 Page Cache 中有新日志时触发
这种方法只要事务提交成功,redo log记录就一定在硬盘里,不会有任何数据丢失,也是MySQL默认的刷盘策略,保证事务持久性

为什么参数为2的时候MySQL挂机不会丢失数据,而系统宕机会丢失数据呢?
因为这种策略下已经提交的事务直接写入系统内核缓存,MySQL挂了不会导致内核缓存清除
而Linux系统中5秒检查一次脏页,30秒未刷盘的脏页会被操作系统刷盘,或者等待MySQL重启刷盘,所以之后还是可以持久化数据页
但是操作系统宕机那系统内核缓存自然清空,数据丢失
而参数为1时,每次提交的事务都会被刷盘,所以不会丢失任何数据

日志文件组:
硬盘上记录redo log的文件一般是以日志文件组的形式出现的,比如可以配置为一组4个文件,每个文件的大小是 1GB,整个 redo log 日志文件组可以记录4G的内容
它采用的是环形数组形式,从头开始写,写到末尾又回到头循环写
在这里插入图片描述

在这个日志文件组中还有两个重要的属性,分别是write poscheckpoint分别代表当前记录位置当前要擦除的位置,后者也可以说是最后一次成功刷盘的脏页对应的日志点

  1. 每次进行redo log刷盘,write pos就会后移
  2. 每次MySQL加载日志文件组恢复数据,就会清空已经加载了的日志记录,即把checkpoint后移
  3. checkpoin推进机制:正常情况下Innodb存储引擎通过配置参数控制脏页刷盘以小批量、异步方式进行刷盘,推进checkpoint
    高负载场景可能触发同步刷盘,导致性能波动

那这时候就有同学要懵比了,你说的第一点我还看的懂,redo log写入一点write pos就后移一点嘛,很好理解,但是你这个checkpoint后移和推进是个什么意思?

重点来了!!!(新手最容易搞混的地方)
如果你产生了这个问题,那么恭喜你,成功地没搞清楚redo log刷盘和数据页(脏页)刷盘的概念(虽然作者刚学的时候也没看明白,一脸懵逼)

这里checkpoint所说【最后一次成功刷盘的脏页的日志点】,是指我们redo log日志【刷盘】到数据页
我们前面所说的 “持久化,刷盘” 只是说短期内提交的的事务 持久化刷盘 到 redo log日志文件 里,实际存储数据页的地方是前文提到的.ibd文件
我们可以认为日志文件只是作为一个中转站把记录的数据(或者说是脏页)【刷盘】到实际数据页中

刷盘
刷盘
事务
Redo Log
.idb 文件

这里就涉及到了一个概念:WAL(Write-Ahead Logging),对数据页修改之前必须先写入日志,而MySQL采用的就是这种技术,先把修改写入日志文件,再根据日志文件修改数据页

最后一个简单小问题:我们上面已经区分了 redo log日志刷盘 和 数据页刷盘的区别了,那么为什么在MySQL中我们要先修改日志进而修改数据页,而不是直接一步到位刷盘数据页呢?
答:
因为日志是顺序写,并且只包含数据修改的操作,比如数据页号,磁盘偏移量,更新值等少量数据
而修改的数据页是随机写的,并且完整数据页包含很多未涉及修改的无效数据,性能差

  • 下面我们要讲binlog了,先提前写个前置知识点,但是这里不是重点,略作了解即可,也可以先跳过,等产生疑问了再回来
    MySQL Server 层:连接层,SQL执行层
    InnoDB 存储引擎层:内存架构,磁盘架构,后台线程

    对比维度MySQL Server 层InnoDB 存储引擎层
    核心职责处理 SQL 解析、优化、执行等跨存储引擎的通用功能负责数据存储、索引管理、事务处理(ACID)、并发控制等底层操作
    关键组件连接器、查询缓存、解析器、优化器、执行器缓冲池(Buffer Pool)、Redo Log、Undo Log、Change Buffer、自适应哈希索引等
    数据存储方式不直接存储数据,仅通过存储引擎接口操作数据数据以页(16KB)为单位存储在 .ibd 文件(独立表空间)或共享表空间中,支持 B+ 树索引结构
    事务支持不直接处理事务,依赖存储引擎实现支持事务的原子性(Undo Log)、持久性(Redo Log)、隔离性(MVCC)
    性能优化重点SQL 语句优化、查询缓存管理内存缓冲池管理、日志顺序写入、脏页异步刷盘、索引合并等
  • binlog(归档日志):MySQL 数据库的数据备份、主备、主主、主从都离不开 binlog,需要依靠 binlog 来同步数据,保证数据一致性
    binlog 是逻辑日志,记录内容是语句的原始逻辑,类似于“给 ID=2 这一行的 c 字段加 1”,属于MySQL Server
    在这里插入图片描述

    binlog 日志有三种格式,可以通过binlog_format参数指定:statementrowmixed
    statement:指定statement,记录的内容是SQL语句原文,比如执行一条update T set update_time=now() where id=1,但是有个问题,update_time=now()这里会获取当前系统时间,直接执行会导致与原库的数据不一致
    row:为了解决这种问题,我们需要指定为row,记录的内容不再是简单的SQL语句了,还包含操作的具体数据,row格式记录的内容看不到详细信息,要通过mysqlbinlog工具解析出来,这种方式可靠性高,但是比较占用空间,恢复与同步时会更消耗 IO 资源,影响执行速度
    mixed:所以就有了一种折中的方案,指定为mixed,记录的内容是前两者的混合。
    MySQL 会判断这条SQL语句是否可能引起数据不一致,如果是,就用row格式,否则就用statement格式
    binlog的写入时机,如图
    在这里插入图片描述

    writefsync的时机,可以由参数sync_binlog控制,默认是1
    0的时候,表示每次提交事务都只write,由系统自行判断什么时候执行fsync
    1则表示每次提交事务都fsync,也可以设置为N,表示提交N个事务才fsync

  • 两阶段提交(2PC):
    在执行更新语句过程,会记录redo logbinlog两块日志,以基本的事务为单位
    redo log在事务执行过程中可以不断写入(主要是buffer一直是同步写的,有后台线程每秒刷盘,不强调事务的原子性)
    binlog只有在提交事务时才写入(保证原子性),所以redo logbinlog的写入时机不一样
    从设计初衷上来说:redo log侧重崩溃恢复效率,需尽早记录修改;binlog侧重数据一致性,需确保逻辑操作完整

    两者的差异就会产生问题:如果redo log正常写完,而binlog写入期间发生异常,数据修改失败,那么MySQL主库加载redo log数据正常,而从库备库加载binlog数据丢失,导致数据不一致
    为了解决两份日志之间的逻辑一致问题,InnoDB 存储引擎使用两阶段提交(2PC)方案:将 redo log 的写入拆成了两个步骤preparecommit,这就是两阶段提交
    在这里插入图片描述
    这样处理就能解决两个日志数据不一致的问题了
    一、如果写入binlog出错无数据,MySQL发现redo log还是prepare阶段,且无binlog记录,则回滚该事务
    二、如果redo log设置commit出错,未标记成commit,但是能通过事务id找到对应的 binlog 日志,所以 MySQL 认为是完整的,就会提交事务恢复数据,不会回滚事务
    两种情况均可以解决

  • undo log(回滚日志):
    实现事务回滚:事务处理过程中,如果出现了错误或者用户执行回滚操作,MySQL 可以利用undo log中的历史数据将数据恢复到事务开始之前的状态。
    实现 MVCC:MVCC 是通过 ReadView + undo log 实现的。undo log 为每条记录保存多份历史数据,MySQL 在执行快照读的时候,会根据事务的 Read View 里的信息,顺着 undo log 的版本链找到满足其可见性的记录

    MySQL有默认的自动提交机制(Autocommit):MySQL默认启用autocommit模式(autocommit=ON),即每条增删改语句会被视为一个独立的事务。执行成功后,MySQL会自动提交事务;若执行失败,则自动回滚
    实现这一机制就是undo log,它保证了事务的的原子性
    undo log 属于逻辑日志,记录的是 SQL 语句,在执行事务的时候会记undo log日志,并且是记录相反的操作用于回滚,比如执行的是insert操作,那么undo log里面的就是delete操作

    回想前面的redo log操作,我们提到了后台线程每秒自动刷盘,而我们的事务执行的时候又是同步写入redo log buffer的,也就是说会出现事务未提交就被刷盘的情况,这种情形也是通过undo log实现回滚的

    最后引用一下别人对undo log刷盘的介绍,本文就到此为止啦~

    很多人疑问 undo log 是如何刷盘(持久化到磁盘)的?
    undo log 和数据页的刷盘策略是一样的,都需要通过 redo log 保证持久化。
    buffer pool 中有 undo 页,对 undo 页的修改也都会记录到 redo log。redo log 会每秒刷盘,提交事务时也会刷盘,数据页和 undo 页都是靠这个机制保证持久化的。

<think>好的,我现在需要回答用户关于MySQL数据页是随机写而redolog是顺序写的原因。首先,我需要明确用户的问题,并理解他们可能对MySQL的存储机制和日志系统不太熟悉,因此需要从基础开始逐步解释。 首先,我应该回忆一下MySQL的架构,特别是InnoDB存储引擎的部分。数据页通常是存储在表空间文件(如ibdata1或独立的.ibd文件)中的,而redo log则是用于崩溃恢复的日志文件。用户的问题涉及到这两种写入方式的区别,即为什么数据页是随机写,而redo log是顺序写。 接下来,我需要分析数据页redo log的不同用途。数据页是实际存储数据的结构,当数据被修改时,这些修改首先在内存中的缓冲池(buffer pool)中进行,然后异步新到磁。而redo log记录的是对数据页的物理修改,用于在系统崩溃后恢复未新的数据页。 然后,我应该考虑磁I/O的特性。顺序写比随机写快得多,因为顺序写不需要频繁移动磁头(在HDD的情况下)或减少SSD的写入放。因此,MySQL的设计者可能将redo log设计为顺序写,以提高写入性能,确保事务的持久性(ACID中的D)能够高效完成。 对于数据页,由于数据页分散在表空间的各个位置,当缓冲池中的脏页需要新到磁时,这些页可能分布在不同的位置,导致随机写。此外,数据页通常是异步的,由后台线程处理,而redo log是在事务提交时必须完成的,因此需要更高的效率,顺序写更适合这种高频、必须快速完成的场景。 可能还需要考虑Checkpoint机制,它决定了哪些脏页需要,而redo log的循环使用方式也支持顺序写入。此外,redo log的文件是预先分配的,以固定小循环使用,这样写入时总是追加到文件的末尾,形成顺序写。 用户可能存在的疑问包括:为什么数据页不能也顺序写?答案可能是因为数据页的位置由主键、索引等结构决定,无法保证连续,而redo log的结构设计允许连续写入。此外,数据页策略(如LRU算法)可能导致随机访问模式。 需要确保解释清晰,避免技术术语过多,但又要准确。可能需要用比喻,比如将redo log比作连续记录操作的日志,而数据页像是分散在仓库中的货物,每次存取不同位置的货物导致随机访问。 最后,总结时强调两种写入方式的设计选择是基于它们不同的用途和性能优化考虑,确保MySQL在事务处理和崩溃恢复上达到高效和可靠。</think>### 分步解析:MySQL 数据页(随机写)与 Redo Log (顺序写)的本质区别 --- #### **一、核心结论** - **数据页(随机写)**:数据页在磁上存储位置不连续,修改时需要按实际物理位置写入,导致随机I/O。 - **Redo Log (顺序写)**:Redo Log 以追加方式写入固定小的文件,形成连续地址写入,天然支持顺序I/O。 - **本质原因**:两者的存储结构和用途不同,决定了写入方式的差异。 --- #### **二、底层机制对比** ##### **1. 数据页(随机写)** **核心流程**: 1. **修改发生**:事务修改数据时,先更新内存中的缓冲池(Buffer Pool)数据页。 2. **脏页标记**:修改后的数据页标记为“脏页”(Dirty Page)。 3. **触发**: - **后台线程周期性**(如每秒一次)。 - **缓冲池空间不足时强制**(LRU机制)。 4. **写入磁**:将脏页按实际物理位置写回表空间文件(`.ibd` 文件)。 **随机写的原因**: - **数据分布不连续**:数据页在表空间中的物理位置由主键、索引结构决定,修改后的脏页可能分布在磁的不同位置。 - **异步**:操作是批量且延迟的,无法保证写入顺序。 - **碎片化写入**:频繁的增删改操作导致表空间文件产生碎片,进一步加剧随机I/O。 ```text 示例:表空间文件(.ibd) +---------+---------+---------+ | 页1 (A) | 页2 (B) | 页3 (C) | --> 修改页1和页3后 +---------+---------+---------+ ↓ 磁写入:页1(位置0) → 页3(位置2) → 随机I/O ``` ##### **2. Redo Log (顺序写)** **核心流程**: 1. **日志记录**:事务提交前,将修改操作按顺序写入 Redo Log Buffer。 2. **策略**: - **策略1(默认)**:事务提交时强制(`innodb_flush_log_at_trx_commit=1`)。 - **策略2**:每秒(`innodb_flush_log_at_trx_commit=0/2`)。 3. **写入磁**:Redo Log 以追加方式写入日志文件(`ib_logfile0`、`ib_logfile1`)。 **顺序写的原因**: - **固定文件结构**:Redo Log 文件是预先分配的环形文件,写入时始终追加到当前文件末尾。 - **物理连续写入**:日志条目按事务提交顺序连续写入,无地址跳跃。 - **崩溃恢复依赖**:顺序写保证日志的完整性,避免恢复时出现空洞。 ```text 示例:Redo Log 文件 +-------+-------+-------+-------+-------+ --> 始终追加写入 | Log1 | Log2 | Log3 | Log4 | ... | +-------+-------+-------+-------+-------+ 写入方向:→ 顺序追加 ``` --- #### **、性能与设计权衡** | **维度** | **数据页(随机写)** | **Redo Log (顺序写)** | |------------------|--------------------------------------------|-------------------------------------| | **写入速度** | 慢(HDD随机I/O吞吐量约100 IOPS) | 快(HDD顺序I/O吞吐量约200 MB/s) | | **可靠性** | 异步可能丢失未提交数据 | 事务提交时强制保证持久性 | | **设计目标** | 平衡内存与磁性能,减少频繁开销 | 最化写入吞吐量,保障事务持久性 | | **优化手段** | 缓冲池(Buffer Pool)、Change Buffer | 组提交(Group Commit) | --- #### **四、关键问题解答** ##### **1. 为什么 Redo Log 不设计为随机写?** - **性能需求**:事务提交要求高吞吐量,顺序写速度是随机写的数百倍。 - **崩溃恢复**:顺序写保证日志连续性,恢复时无需跳跃读取。 ##### **2. 数据页能否改为顺序写?** - **不可行**:数据页的物理位置由索引结构(如B+树)决定,无法强制连续存储。 - **代价过高**:若强制按顺序存储,会破坏索引的高效查询特性。 ##### **3. Redo Log 如何避免写满?** - **Checkpoint 机制**:定期将 Redo Log 中的已提交修改数据页,释放日志空间。 - **循环复用**:Redo Log 文件以环形结构复用,覆盖已的旧日志。 --- #### **五、总结** - **数据页随机写**:由数据存储的物理分布特性决定,是权衡查询性能与开销的结果。 - **Redo Log 顺序写**:为满足事务持久性与高吞吐量需求,利用磁顺序I/O优势。 - **设计哲学**:通过 Redo Log 的**顺序写**牺牲少量日志空间,换取事务提交的高性能;通过数据页的**随机写**容忍延迟,保障查询效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值