讲了这么多年 MySQL,InnoDB 的 REDO LOG 依然是被问得最多的知识点,也是排查故障时最容易出问题的环节。尤其在 MySQL 8.0 里,redo log 的架构和参数体系都经历过一轮明显调整,还沿用老版本那套思路去运维,很容易掉进坑里。
这篇文章我以自己的实战经验为底,把 InnoDB REDO LOG 从“为什么存在”到“物理结构长什么样”,再到“log buffer 怎么写、什么时候刷盘、崩溃恢复怎么工作”完整串一遍,最后给出一套可落地的配置和排查方法。整个过程基于 MySQL 8.0,但原理部分对 5.7 同样适用。
适合刚接触 InnoDB 内核机制的人建立整体概念,也适合已经被线上 redo log 容量打满、恢复耗时过长折磨过的运维老手对照自查。
1. 先搞清楚 REDO LOG 到底在解决什么问题
1.1 没有 redo log 的数据库会怎样
数据库要保证事务的持久性,核心要求是:只要事务提交成功,数据就不能丢。最直接的做法是每次提交时把修改后的数据页写回磁盘,问题是一张普通 InnoDB 表的数据页默认 16KB,而一次 UPDATE 可能只需要修改其中几十个字节。把完整数据页刷下去,写放大是几百倍,同时数据页在磁盘上是随机分布的,每次提交都伴随大量随机 I/O,性能会跌到没法用。
redo log 的出现,就是把这笔账重新算了一遍:事务提交时不刷数据页,而是把“这次事务改动了哪些页、偏移量多少、旧值变成什么新值”这些信息顺序写入日志文件。顺序写 SSD 的延迟通常能控制在几十微秒级别,而随机写数据页往往要几百微秒甚至毫秒级。用一个数量级的速度差,换来持久性保证,这就是 WAL(Write-Ahead Logging,预写日志)的核心逻辑。
也就是说,真正的数据页可以继续留在内存的 buffer pool 里,等到后台线程慢慢刷盘。只要 redo log 里有完整记录,即使数据页还没落盘时数据库崩溃,重启后也能通过重放日志把数据恢复出来。
1.2 WAL 机制:一个反直觉的设计
刚接触数据库内核的人常常会问:既然日志也是写磁盘,为什么直接写数据页反而不行?其实差别全在 I/O 模式里。
日志写盘是追加式的。InnoDB 的 redo log 文件是循环使用的,写入位置基本只在当前文件尾部和下一个文件开头之间移动,磁盘寻道基本可以忽略。数据页写盘是随机的,一张表的数据分布在很多表和索引里,今天这个页、明天那个页,盘片或闪存颗粒要频繁切换位置。机械盘上顺序写和随机写的差距可以到两个数量级,SSD 上随机写虽然快了很多,但涉及写放大和垃圾回收,情况也没本质变化。
另一层是安全下界的控制。WAL 的约定是先写日志、后写数据,而且日志必须完整落盘,事务才能提交。只要这条顺序被严格保证,数据页写盘这件事就不需要跟事务提交同步,随便什么时候写都可以,因为日志里总有备份。这种“把随机 I/O 转换成顺序 I/O、把关键路径上的 I/O 量降到最低”的设计思路,是整个 InnoDB 性能的基石。
1.3 redo log、binlog、binlog、undo log 的分工边界
很多初学者会把 redo log、binlog、undo log 混在一起,实际上三者解决的问题完全不同。
redo log 是 InnoDB 存储引擎层的物理日志,记录的是“某个表空间、某个页号、某个偏移量处发生了什么样的修改”。它服务于崩溃恢复,保证已提交事务不丢失,由 InnoDB 自己管理,事务提交时通过 innodb_flush_log_at_trx_commit 参数控制刷盘策略。
binlog 是 MySQL Server 层的逻辑日志,记录的是 SQL 语句或行级变更的二进制表示,服务于主从复制和时间点恢复。两者还有一个关键区别:redo log 是循环写、有空间上限,binlog 是追加写、持续累积。
undo log 则是逻辑日志,记录了事务修改前的数据版本,用于事务回滚和 MVCC 多版本控制。它和 redo log 面对的方向正好相反:redo 记录“改成了什么”,undo 记录“原本是什么”。
一个事务提交的完整链路里,redo log 和 binlog 之间还有分布式一致性协调,通过两阶段提交保证物理日志和逻辑日志一致。这块在 MySQL 8.0 里表现为 prepare 阶段写 redo log、commit 阶段写 binlog 和 redo log 的 commit 标记。理解这条链路,后面排查主从延迟和崩溃恢复问题会顺手得多。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. MySQL 8.0 的 REDO LOG 磁盘结构和文件布局
2.1 从 log group 到 #innodb_redo 目录
MySQL 5.7 及更早版本里,redo log 默认以 ib_logfile0、ib_logfile1 这种命名存放在数据目录,由 innodb_log_group_home_dir 指定路径,文件大小和数量分别由 innodb_log_file_size 和 innodb_log_files_in_group 控制。
到了 MySQL 8.0.30,这个体系被重构了。redo log 文件挪到了数据目录下的 #innodb_redo 文件夹里,文件名变成 #ib_redo1、#ib_redo2 这样,理论上不再需要人工去数文件个数和算总体积。这个文件夹在实例启动时自动创建,里面的 redo 文件由 InnoDB 内部动态管理,随着写入和检查点推进自动增加或删除。
所以到了 8.0.30 之后,如果还在用老参数 innodb_log_file_size 去调 redo 容量,会发现配置根本不生效。正确做法是使用新的容量控制参数,下面单独说。
从文件名这一层变化能看出 8.0 的设计趋势:尽可能减少 DBA 手工干预,让引擎自己感知负载并动态调整。但对熟悉老版本的人来说,这个变化初期会让人不太适应——我就是从一次线上事故里才彻底搞明白这套新机制的,后面故障排查部分会详聊。
2.2 innodb_redo_log_capacity 参数
MySQL 8.0.30 引入 innodb_redo_log_capacity 来统一控制 redo log 总容量,默认值是 104857600 字节,也就是 100MB,可设置范围是 8MB 到 128GB。官方建议将所有 redo 文件的总大小保持在这个容量值附近,InnoDB 内部会根据当前容量目标自动决定保留多少个 #ib_redoN 文件。
这个参数是动态的,可以执行 SET GLOBAL innodb_redo_log_capacity = 8589934592; 在线调整,不需要重启。InnoDB 会在后台逐步收缩或扩展 redo 文件数量,让总容量逐渐接近新目标。所以线上如果发现 redo 空间不足,不需要像 5.7 时代那样先干净关闭、删日志、改配置再启动,直接在线放大容量即可,这对高可用环境来说是非常实用的改进。
同时,8.0.30 还提供了 innodb_log_writer_threads 参数。打开后,写 redo log 的动作统一交给后台 log writer 线程执行,用户线程只负责把日志内容拷贝到 log buffer 并登记自己需要等待的刷盘位置,不再各自去发起 write 系统调用。这样能显著降低高并发下写日志的锁竞争和上下文切换。
2.3 redo log block 的物理格式
redo log 不是一串无边界的字节流,而是按固定大小的 block 组织。每个 block 是 512 字节,正好匹配传统磁盘扇区大小,这样设计是为了保证日志写入的原子性,避免写入过程中出现半个块落盘的情况。
block 头部有 12 字节的 header,四个字节的 block number 表示块号,两个字节的 data length 表示当前块中实际有效的数据长度,两个字节的 first record group offset 用于标记事务日志记录在块内的起始位置,剩下四个字节是 checksum,用于校验块内容是否完整。block trailer 占 4 字节,存放校验值。
每一条具体的 redo 记录被称为 redo log record,由日志类型、表空间 ID、页号、记录长度和实际数据组成。恢复时 InnoDB 扫描 block,依靠 checksum 判断哪个块是完整写入的,从完整块开始重放,不完整的块直接丢弃,因为那部分事务本来就不应该被当成已提交。
理解 block 结构对排查恢复问题很有帮助。比如检查点之后最早的那个块数据不完整、checksum 校验失败时,InnoDB 会选择从上一个完整块开始处理,这也是为什么 redo log 文件设置太小会导致额外写放大和恢复时间变长的底层原因之一。
3. REDO LOG BUFFER:写入链路的第一个中转站
3.1 log buffer 的结构与写入流程
redo log buffer 是内存中一块连续区域,专门用来暂存事务生成的 redo 记录。MySQL 8.0 中由 innodb_log_buffer_size 控制,默认值是 16MB,这个参数在运行中是只读的,调整需要重启实例。
用户线程在修改缓存页时,会先把对应的 redo 记录写入 log buffer,而不是直接写磁盘。这个过程是一段内存拷贝,极快,但需要注意并发问题。多个事务同时生成 redo 记录时,需要通过 log_sys->log_mutex 等锁机制串行化追加位置。在 log_writer_threads 开启后,竞争点被进一步弱化,用户线程的最小操作单元是 log buffer 中预留的一块区域,各自负责把数据拷贝进去,最后由后台线程统一处理真正的磁盘写入。
buffer 里保存的是新产生的 redo 记录,而磁盘上保存的是已经落盘的 redo 记录。只要 buffer 中的记录没有刷到磁盘,对应的持久性就还停留在内存级别。事务提交时决定是否立即把 buffer 内容刷盘,由 innodb_flush_log_at_trx_commit 参数控制。
3.2 什么时候会触发刷 log buffer
除了事务提交这个最核心的触发点,还有几个常见场景会把 log buffer 里的内容刷到磁盘。
第一种是 log buffer 空间不足。redo 记录还在源源不断产生,如果事务太多、单个事务修改量太大,buffer 容量被耗尽,就必须把已有内容刷出去才能腾出空间。这个等待事件在性能监控里通常表现为 log buffer 相关的等待,大量出现时一般意味着 buffer 太小或刷盘太慢。
第二种是后台线程周期性刷盘。InnoDB 的后台线程会定期把 log buffer 中的内容刷到磁盘,默认情况下大约每 1 秒执行一次,由 srv_flush_log_at_trx_commit 相关逻辑控制。这种周期性刷盘的主要目的是减少实例崩溃时的日志丢失范围,以及在 binlog 同步场景下维持一定的刷盘节奏。
第三种是 checkpoint 推进前需要确保日志已落盘。执行检查点意味着要把某个 LSN 之前的日志和数据页都固化到磁盘,如果日志还留在 buffer 里,检查点就无法安全推进,所以刷新日志也是检查点流程的一部分。
3.3 组提交如何提升写入效率
组提交(group commit)是 InnoDB 优化 log buffer 刷盘效率的核心手段。原理很简单:多个事务在几乎同一时刻提交,不再各自刷一次磁盘,而是合并成一次刷盘操作。
具体链条是这样的:事务在 commit 阶段获取到 redo log 的写入位置,把自己的日志和后续排队进来的其他事务日志一起拷贝进 buffer,然后等待一次落盘。落盘完成后,这一批次的所有事务一起返回成功。这样原本需要刷 100 次磁盘的 100 个并发事务,合并后可能只需要刷几次,吞吐量提升非常明显。
在 MySQL 8.0 中,binlog 和 redo log 的两阶段提交也整合进了组提交机制,分别有 leader 和 follower 的分工。这里的关键调优点在于 binlog_group_commit_sync_delay 和 binlog_group_commit_sync_no_delay_count,控制组提交的等待窗口。如果这两个参数都设为 0,每次提交都可能立刻触发刷盘,组的大小完全看瞬时并发,吞吐量会受限;如果适当调大 delay,可以获得更大的提交组,代价是单个事务的提交延迟上升,需要根据业务容忍度平衡。
3.4 log buffer 容量应该怎么设置
log buffer 大小的选择,核心逻辑是让它在绝大多数时间里够用,且不被频繁刷盘拖累,又不能设置过大导致内存浪费。
最简单的方法是按“高峰期每秒产生的 redo 量”来估算。比如一个高峰期写入量约 50MB/s 的实例,如果每秒刷盘一次,那么 16MB 的默认 buffer 明显不够,至少需要 64MB 甚至 128MB。可以用 SHOW ENGINE INNODB STATUS 里的 Log 部分观察当前日志写入量,也可以用 performance_schema 中的 events_waits_summary_global_by_event_name 查看 log buffer 相关的等待事件。
实际场景中,我见过最典型的问题是超大事务。一个批量 UPDATE 修改几百万行,过程中产生的 redo 记录可能有几百 MB,如果 buffer 只有 16MB,这个事务的执行过程会被迫不断触发刷盘,拖慢执行速度。这种情况下设置 256MB 或更大 buffer,往往能显著改善大事务场景的耗时。
但要清楚一点:log buffer 再大,也不能替代 redo log 文件容量。buffer 是内存中的缓存,文件是持久化存储,两者维度不同,超配 buffer 不能解决 log file 打满的问题。
4. LSN 和 CHECKPOINT:崩溃恢复的坐标体系
4.1 LSN 到底代表什么
LSN(Log Sequence Number,日志序列号)是 InnoDB 中贯穿全局的核心概念,简单理解,就是 redo log 的字节流位置计数器。每个 redo 记录在写入时都会消耗若干字节的 LSN 空间,LSN 单调递增。InnoDB 中各种关键位置,比如 log buffer 写到了哪里、日志文件写到了哪里、哪些脏页已经刷盘,全部用 LSN 来标记。
举几个实际例子:buf_pool 中的每个数据页头里都存着一个 LSN,表示这个页最近一次被修改对应的日志位置;flush list 中的脏页按 LSN 排序,刷盘时优先刷 LSN 较小的页。崩溃恢复时,只需要从最近一次 checkpoint 的 LSN 开始重放日志,就能把数据恢复到崩溃前的状态。
LSN 的计算有一点要注意:它表示的是日志写入的字节位置,不是日志条数。因此不能通过“LSN 差值除以事务数”来评估单个事务日志大小,而应该通过 SHOW ENGINE INNODB STATUS 中 Log sequence number、Log flushed up to、Last checkpoint at 这三组值之间的关系来判断系统状态。
4.2 checkpoint 是如何推进的
checkpoint 指的是这样一个位置:在这个 LSN 之前的所有 redo log 对应数据页变更,都已经全部刷新到了磁盘。理论上,这个位置之前的 redo 日志不再需要用于崩溃恢复,可以安全覆盖。
InnoDB 的 checkpoint 由后台线程在满足一定条件时触发。主要条件包括:redo log 文件可用空间不足,需要回收;buffer pool 中的脏页比例超过阈值;系统空闲时的周期性推进;以及最后一次刷盘距离当前时间超过一定周期。每次执行 checkpoint 时,InnoDB 从 flush list 中找出 LSN 最靠前的脏页,将其刷盘,然后逐步更新 checkpoint LSN。
需要强调一个容易误解的地方:checkpoint 不是瞬间完成的,它是一个持续刷脏页并推进水位的过程。如果写入压力很大、脏页生产速度超过刷盘速度,checkpoint LSN 就追不上当前写入 LSN,两者之间的 redo 空间不断累积。当 redo log 文件被新日志写满、旧日志又因为对应脏页未刷完而无法覆盖时,就会出现“redo log 打满”的经典问题。
4.3 崩溃恢复的完整流程
MySQL 崩溃后重新启动时,InnoDB 会执行崩溃恢复,分为三个阶段。
第一阶段是定位。InnoDB 从 redo log 文件中找到最近一次 checkpoint 对应的 LSN,这个位置之后的所有日志都是需要重放的范围。
第二阶段是扫描和重放。InnoDB 从 checkpoint LSN 开始顺序扫描 redo log,将日志记录解析出来,对涉及的数据页重新应用修改。如果某个数据页在崩溃前已经刷盘,重放操作会重复执行一次修改,但 redo 记录本身是幂等的,多次应用结果一致,所以没有副作用。这一阶段是恢复耗时的主要来源,耗时与 checkpoint 到日志末尾的长度成正比,与 redo log 总量也基本成正比。
第三阶段是清理。重放完成后,InnoDB 会把尚未刷盘的脏页继续刷盘,更新 checkpoint LSN,清理临时表空间等内部结构,然后打开对外服务。
恢复耗时是生产环境最关注的问题之一。日志量越大,恢复越慢。一个写压力很大的实例,如果 redo log 容量设置过大,在崩溃恢复时扫描和重放的时间会明显变长,导致不可用窗口变长。这也是为什么 redo log 容量并非越大越好的原因,需要在运行容量和恢复速度之间做平衡。
5. 参数配置、状态监控和性能调优
5.1 关键参数速查
整理一份我日常调优时使用的参数清单,基于 MySQL 8.0.30 及以上版本。
innodb_log_buffer_size 控制 redo log buffer 大小,默认 16MB,只读参数,需重启生效,对应高并发或大事务场景应调大。innodb_redo_log_capacity 控制 redo log 总容量,默认 100MB,动态可调,建议按实例高峰期 30~60 分钟内产生的日志量估算。innodb_flush_log_at_trx_commit 取值为 0、1、2,默认是 1 表示每次提交都刷盘,安全性最高但延迟最高;0 表示由后台线程每秒刷一次,性能最好但可能丢最后 1 秒日志;2 表示提交时写入操作系统缓存,每秒刷一次磁盘,性能和数据安全性折中。
innodb_log_writer_threads 在 8.0.30 后出现,默认 ON,让专用线程执行写盘操作。innodb_log_write_ahead_size 控制日志文件预写块大小,默认 8192 字节,主要用于减少写放大,一般保持默认。innodb_flush_log_at_timeout 是后台刷盘周期,默认也是 1 秒,可以和 trx_commit=0 配合使用。
补充一点老版本迁移的注意点:如果你从 5.7 升级到 8.0.30 以上,配置里还写着 innodb_log_file_size,这个参数已经被忽略。需要设置 innodb_redo_log_capacity 并且迁移完成后把旧参数从配置文件中移除,避免产生误导。
5.2 怎么看 redo log 的运行状态
SHOW ENGINE INNODB STATUS 是排查 redo log 问题的第一入口,重点看 LOG 段。我复述一个典型输出里最关键的几行,通常长这样:
Log sequence number 表示当前写入日志的最大 LSN,也就是最新产生的日志位置。Log flushed up to 表示已经刷到磁盘的 LSN。Last checkpoint at 表示最近一次 checkpoint 的位置。
根据这三个值的相对关系能判断系统状态。如果三者非常接近,说明刷盘和检查点都很及时,系统很健康。如果 Log sequence number 和 Log flushed up to 差距持续拉大,说明刷盘跟不上写入速度,要么磁盘性能太差,要么 log buffer 太小导致频繁写等待。如果 Log flushed up to 和 Last checkpoint at 之间的差距持续拉大,说明脏页刷盘跟不上,系统正在积累大量未刷盘数据,这种状态下 redo log 空间消耗会快速上升。
另一个好用的视角是 performance_schema 的等待事件统计,重点看 wait/io/innodb/log_files 相关的等待,或者直接查 information_schema.innodb_metrics 里 log 相关的计数器。生产环境可以配合 Prometheus 抓取 mysqld_exporter 的指标,长期监控 log position 的增长速率和 checkpoint 位置,设置阈值告警。
5.3 一次 redo log 瓶颈排查实录
之前接手过一个线上实例,现象是业务高峰期出现大量更新慢查询,监控看磁盘 I/O 也没到极限,但数据库整体响应明显变差。
排查过程是,先看 SHOW ENGINE INNODB STATUS,发现 Log sequence number 和 Last checkpoint at 的差距非常大,而且持续增长,说明脏页刷盘跟不上。接着检查 innodb_redo_log_capacity,当时只有默认的 100MB,对比业务写入量,高峰期大约每 10 分钟就会产生 100MB 的 redo 日志,意味着 redo log 每 10 分钟就会被新日志覆盖一轮,checkpoint 线程被迫频繁刷脏页来腾空间,刷盘速度一旦跟不上,写线程就会阻塞。
定位到问题后,先把 innodb_redo_log_capacity 在线调大到 2GB,脏页积累缓下来了,再把 innodb_log_buffer_size 从 16MB 调到 128MB,减少大事务造成的 buffer 刷盘压力,然后在业务低峰期把 my.cnf 里的配置持久化,等待重启生效。调整后高峰期的更新慢查询基本消失,checkpoint LSN 的追赶速度正常了。
这个案例最有价值的经验是:redo log 容量偏小不会直接报错,它的表现方式是性能劣化,因为 InnoDB 在 redo 空间不足时会内部触发同步刷脏和等待,而这些等待不一定反映在磁盘 I/O 利用率上,却会拖慢所有写入请求。
6. 常见故障与排查技巧实录
6.1 redo log 写满导致的全实例卡顿
redo log 文件被写满而 checkpoint 又无法推进时,所有需要写 redo log 的事务都会被阻塞,表现为全实例“卡死”,监控能看到大量线程卡在 log 相关等待上。
这种问题的根因通常是两类。一类是磁盘刷盘速度过慢,脏页刷不出去,checkpoint 无法推进,多见于机械盘或云盘性能突降。另一类是存在超大事务或大量并发写,产生 redo 的速度远远超过刷盘速度。排查时先看 innodb_redo_log_capacity 是否偏小,再看脏页比例(Innodb_buffer_pool_pages_dirty 除以总页数),再确认磁盘实际 I/O 能力是否达标。
处理方法分两步:第一步是止血,在线调大 innodb_redo_log_capacity,给 checkpoint 更多空间缓冲;第二步是找根因,看是不是有大事务、长事务或锁等待导致脏页长期无法刷盘。8.0.30 之前的版本没法在线调整,只能尽快处理完阻塞事务,等待 checkpoint 自行恢复,或者计划内维护调整参数。所以强烈建议升级到 8.0.30 以上,可以用动态参数兜底。
6.2 实例崩溃恢复耗时过长
实例异常重启后,恢复阶段迟迟起不来,这是 redo log 容量过大带来的典型副作用。崩溃恢复需要从最近 checkpoint 扫描到日志末尾的全部日志,如果日志总量很大而 checkpoint 位置很靠后,恢复时间会很长。
恢复耗时和哪些因素相关?一个是 checkpoint 和日志末尾之间的距离,越大恢复越慢;另一个是涉及数据页的数量,同一 LSN 区间内涉及的不只是日志解析,还包括页的随机读;还有是磁盘性能,日志重放需要读取对应数据页,随机读性能直接影响速度。
调优思路是平衡容量与恢复时间。容量大可以减少运行期刷盘压力、提升写入稳定性,但恢复期代价升高。我的建议是,普通业务实例容量设置为高峰期 30 分钟内日志量的 1.5~2 倍,优先保证运行期性能;对恢复时间有严格 SLA 的实例,容量要适当收小,同时通过调高刷脏频率、增加 buffer pool 刷盘线程来保证 checkpoint 持续推进。
另外要确认 innodb_buffer_pool_dump_at_shutdown 和 innodb_buffer_pool_load_at_startup 是否开启。如果开启了,恢复结束后的预热阶段也属于启动流程的一部分,会让人误以为崩溃恢复还没结束。
6.3 日志不落盘的数据丢失风险
innodb_flush_log_at_trx_commit 设置为 0 或 2 时,事务提交的持久性并不是绝对的。设置为 0 时,事务提交后 redo 只留在 log buffer 里,依赖后台线程每秒刷盘;如果在该时机点发生宕机,最后 1 秒的提交事务可能全部丢失。设置为 2 时,提交时 redo 写入操作系统缓存,但依赖操作系统在几秒内刷到磁盘;如果整个机器宕机或断电,同样可能丢失最近未刷盘的内容。
这个参数是数据安全与性能的直接权衡点。很多支付类业务要求必须每次提交都刷盘,也就是设置为 1;一些允许丢失少量数据的分析或日志类业务,可以把该参数设为 2。设置 0 的情况比较少见,一般只在对性能要求极高、数据丢失容忍度高的场景使用。MySQL 8.0 中,binlog 的 sync_binlog 参数也存在对应关系,主从环境下要同时考虑两者的刷盘策略,否则可能在主库切换时出现数据不一致。
经验上,如果业务要求严格不丢数据,务必保持 innodb_flush_log_at_trx_commit=1 和 sync_binlog=1。这个配置在 SSD 环境下对性能的影响并没有想象中那么大,配合组提交优化,多数业务都能接受。
6.4 一个排查工具和使用技巧
日常工作中最常用的排查命令组合我整理一下,按执行顺序排列:
先查性能基线,SHOW GLOBAL STATUS LIKE 'Innodb_os_log_written'; 看累计写入字节量,过一段时间再查一次,相减可以得到当前 redo 产生速率。再查持久化状态,SHOW ENGINE INNODB STATUS; 看 LOG 段,确认 flush pos 和 checkpoint pos 距离。若差距很大,查脏页比例,SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME IN ('Innodb_buffer_pool_pages_dirty','Innodb_buffer_pool_pages_total'); 计算比例。正常情况下脏页比例通常在 10% 以内,如果持续超过 20%,说明刷脏能力不足或 redo 空间太紧张。
还有一个小技巧:用 SHOW GLOBAL STATUS LIKE 'Innodb_log_waits'; 看累计的 log buffer 等待次数。这个值如果持续增长,说明 log buffer 确实偏小,需要调大 innodb_log_buffer_size。如果它一直不变,说明当前 buffer 容量足够,不要盲目调大白白浪费内存。
7. 最后分享几点个人体会
从我处理过的案例看,绝大多数 redo log 相关故障,都可以追溯到“容量设置不合量级”和“刷盘策略不符合业务要求”两个源头。
容量方面,我的经验是先测出业务高峰期的 redo 产生速率,再反向推导容量需求。方法不难:挑一个高峰期,记录一分钟内 Innodb_os_log_written 的增长量,乘以 60 得到每小时日志量,再乘一个 0.5~1 的系数作为容量目标。例如高峰期每分钟写 30MB redo,每小时就是 1.8GB,容量设置为 1GB 到 2GB 是比较合理的起点,这个体量既不会太频繁触发 checkpoint,恢复时也不会太慢。
刷盘策略方面,不要为了性能盲目放弃持久性。有些业务团队会为了提升写入性能设置 trx_commit=0,但并不知道这意味着断电时可能丢失 1 秒甚至更多已提交事务。在金融、订单、账务这些不允许丢数据的场景,必须使用 1。在确实允许丢失少量数据的场景,至少也要设置 2,并把操作系统层面 fsync 机制调好。
如果你正准备做 MySQL 版本升级,建议你特别关注 8.0.30 这个分界线,把 redo log 相关参数从老体系完整迁移到新体系。这个调整不仅是参数名变了,底层架构也变了,提前理解新机制能帮你省下之后排查故障的大量时间。遇到 redo log 相关的问题,从“写入链路、刷盘时机、容量水位”三个角度入手,基本都能找到系统性答案。
