先说一个容易被搞混的前提:innodb_log_buffer_size 调的不是“redo log 文件的大小”,而是 MySQL 在内存里临时存放 redo log 的那块缓冲区大小。很多刚接触 InnoDB 事务机制的人,看到“log buffer”就以为它决定日志总量,然后一把梭改成 1G,其实完全没必要,改了反而让实例白白吃掉一大块内存。这篇我按实际运维视角把参数拆开讲,重点放在它到底解决什么问题、瓶颈长什么样、什么时候才需要调,以及怎么判断当前值够不够用。
1. 先搞懂 log buffer 在 InnoDB 事务链路里的准确位置
1.1 一次 UPDATE 的日志是怎么从内存走到磁盘的
理解任何参数前,第一步都是搞清它在数据链路里扮演什么角色。innodb_log_buffer_size 控制的是 InnoDB redo log 写入磁盘前的内存缓冲池大小。
执行一条 UPDATE 时,InnoDB 大概做三件事:先把目标页从磁盘读到 buffer pool 里(如果不在),修改内存页标记为脏页,然后把这次修改产生的 redo log 写入 log buffer。注意这句话的顺序:先写日志,后写数据页。真正把脏页刷回磁盘是后台线程的事,可能发生在几秒甚至几分钟后;但 redo log 是事务提交时就必须落盘的,否则事务一旦崩溃就无法重放恢复。
写入链路大致是:
- 事务执行 DML,修改 buffer pool 中的数据页;
- 同步生成 redo log 记录,写入内存中的 log buffer;
- 事务提交时,MySQL 根据
innodb_flush_log_at_trx_commit参数决定把 log buffer 内容刷到 redo log file 的时机; - log buffer 中未及时落盘的日志,靠后台线程周期性刷盘,或在 buffer 写满时强制刷盘。
innodb_log_buffer_size 在第三步起作用,它决定了第 2 步能往内存里“塞”多少日志,也决定了第 3 步刷盘时一次最多能带出去多少数据。
1.2 参数值的官方定义和生效范围
官方文档定义很简单:该参数指定 InnoDB redo log 写入磁盘前使用的缓冲区大小,默认值在 MySQL 5.7 及以前是 16MB,8.0 默认也是 16MB(早期版本 8.0.12 以前默认 16MB,之后一直保持这个值),单位是字节。允许配置范围是 1MB 到 4096MB(4GB),支持动态修改,也可以用启动参数 --innodb_log_buffer_size 指定,或在会话里直接 SET GLOBAL。
注意一个细节:它是 GLOBAL 级别参数,不是 session 级别参数,所以修改后只对新事务和新的日志写入生效,不会立刻影响正在执行中的事务。
这里放一张我自己整理的简化链路图,方便理解参数位置:
code复制[事务DML] → 修改 buffer pool 脏页 → 生成 redo log
↓
[log buffer(当前参数)] ← 内存区域
↓ 提交/满/后台
[redo log file(磁盘)]
1.3 别把它和“undo log”“binlog cache”搞混
MySQL 里带“buffer”和“log”的词有好几个,经常有人问 log buffer 是不是控制 binlog 缓冲区的。这里做一个明确区分:
- redo log + log buffer:负责崩溃恢复,属于 InnoDB 引擎层;
- binlog + binlog_cache_size:负责主从复制和时间点恢复,属于 Server 层;
- undo log:负责事务回滚和 MVCC,不属于 redo log 体系,有独立的回滚段管理。
如果你关心的是主从延迟或超大事务的 binlog 写入性能,应该去看 binlog_cache_size,调 log buffer 是牛头不对马嘴。我在排查一个“事务提交慢”的问题时,就见过有人把 log buffer 从 16M 调到 1G,后来发现真正卡点其实是 binlog fsync 太频繁。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 为什么 8MB/16MB 默认值在高并发场景下会成为瓶颈
2.1 小 buffer 引发地“释放-等待”恶性循环
默认 16MB 听起来不小,但要知道 InnoDB 一个 16KB 的页可以产生多种 redo 记录,批量 UPDATE 几万行时,一次事务产生的 redo 就可能轻松超过几十 MB。更关键的是 log buffer 不只是给单个事务用的,它是所有并发事务共享的环形缓冲区结构。
当 log buffer 满了,新的 redo log 写入就必须等 buffer 释放。释放只有一条路:把日志刷到磁盘。于是“log buffer 满 → 触发刷盘 → 所有写事务等待刷盘完成 → 峰值吞吐掉下去”。这就是传说中的 log buffer 争用。
高并发写入场景下,如果 log buffer 太小,实际表现是:
- InnoDB 内部出现
log buffer space类的等待事件; - 大量并发短事务的 commit 延迟抬升;
- MySQL 的 TPS 出现周期性“抖动”,每次抖动间隔正好对应 log buffer 被写满的时间周期。
在 MySQL 8.0 里可以用 performance_schema 直接观察等待事件,5.7 则主要通过 SHOW ENGINE INNODB STATUS 观察日志相关计数判断。第二种方法我后面会给出具体操作。
2.2 为什么大事务比小事务更容易“撞墙”
一个几十 GB 的批量 UPDATE 或 LOAD DATA 操作,产生的 redo log 最终肯定远超 16MB,日志不可避免地会“边写边刷”。但 buffer 太小时,刷盘次数会变得非常频繁,刷盘本身有 fsync 开销,每一次强制刷盘都会拉长事务总耗时。
但要注意一个反直觉的现象:大事务的 redo 量过大时,增大 log buffer 的帮助有限,因为事务产生的 redo log 迟早要全部落盘,Buffer 只影响“分几批刷”和“刷盘时机”。真正能让大事务受益的场景是:事务本身产生的 redo log 总量略大于 buffer,调大 buffer 后能从“刷几次”变成“一次刷完”,减少 fsync 次数。
举个例子:一个事务产生 20MB redo log,buffer 16MB 时至少要刷两次甚至更多,commit 时最后一次 fsync 前可能还要等中间刷盘完成;buffer 调到 32MB 后,整个事务的日志都在内存里,commit 时只需一次 fsync。这个收益在大批量 ETL 场景下很可观。
2.3 一个被我复现过的真实瓶颈案例
我维护过的一个订单系统,高峰期 TPS 大约 8000~12000,大部分事务是几百字节到几 KB 的短更新。某次上线后 P99 延迟从 30ms 涨到 200ms,查看 SHOW ENGINE INNODB STATUS 的 LOG 部分,我看到的输出如下(节选):
code复制Log sequence number 302409034114
Log flushed up to 302401402135
Last checkpoint at 302398100235
当时没细看数值,直接查了 Innodb_log_waits 和 Innodb_os_log_written 状态变量,发现 Innodb_log_waits 在 10 分钟内增加了 40 多万次,这表示“因 buffer 空间不足导致日志写入等待”的次数异常高。默认 16MB 的 buffer 在每秒钟产生近 2MB redo 的高峰面前撑不住,后台刷盘速度赶不上并发写入。
把 innodb_log_buffer_size 调到 64MB 后,log_waits 基本归零,P99 延迟恢复到正常水平。这个案例可以很直观回答“什么配置值算合理”的问题:不取决于你感觉,而取决于 log_waits 和 redo 生成速率。
3. 怎么判断当前 innodb_log_buffer_size 到底够不够
3.1 用状态变量和性能指标做第一轮判断
网上很多人喜欢直接给“生产环境调成 64M/128M”的经验值,但我想教你先学会看证据,因为不是每个业务都需要加大。判断依据主要有四类:
- Innodb_log_waits(最重要的指标)
sql复制SHOW GLOBAL STATUS LIKE 'Innodb_log_waits';
SHOW GLOBAL STATUS LIKE 'Innodb_os_log_written';
SHOW GLOBAL STATUS LIKE 'Innodb_os_log_pending_fsyncs';
Innodb_log_waits 表示 log buffer 空间不足导致等待的次数。注意这个值是累计值,不是速率,所以要用两次采样差值除以时间来计算增长速率。如果这个值在高峰期持续增长,说明 buffer 不够用;如果长时间不增长或增长极慢,buffer 就不是瓶颈。
实际运维里我一般用 Prometheus 抓 MySQL exporter 的指标,然后观察 innodb_log_waits 和 innodb_os_log_written 的速率曲线,比手工查状态变量直观得多。没有监控体系的同学,可以用下面这个 SQL 在高峰期前后各跑一次:
sql复制SELECT variable_name, variable_value
FROM performance_schema.global_status
WHERE variable_name IN ('Innodb_log_waits', 'Innodb_os_log_written', 'Innodb_os_log_pending_fsyncs');
- redo log 生成速率
结合 Innodb_os_log_written 的增量看每秒产生多少 redo log。假如平均每秒生成 5MB redo,而 buffer 只有 16MB,意味着正常情况 3 秒左右就满了,刷盘压力很大。
计算示例:
- 第一次查询
Innodb_os_log_written= 100GB; - 60 秒后 = 100GB + 300MB;
- 则每秒 redo 生成速率约 5MB/s。
如果出现平均每秒 redo 生成量超过或接近 buffer 大小的 1/5,通常就提示需要调大 buffer 了。
- InnoDB 引擎内部等待事件(8.0 推荐)
performance_schema 的等待事件比状态变量更细粒度,能直接看到等待发生在哪个环节。在 8.0 里可以这样查:
sql复制SELECT EVENT_NAME, COUNT_STAR, SUM_TIMER_WAIT/1000000000 AS wait_ms
FROM performance_schema.events_waits_summary_global_by_event_name
WHERE EVENT_NAME LIKE '%log%buffer%' OR EVENT_NAME LIKE '%log%space%'
ORDER BY SUM_TIMER_WAIT DESC LIMIT 10;
重点看 wait/io/innodb/log_files(刷盘 I/O 等待)和 wait/synch/mutex/innodb/log_buffer_mutex(log buffer 锁等待)。如果后者占比明显,说明 buffer 小导致写入争用;如果前者占比高,说明瓶颈在磁盘 fsync 速度,此时加 buffer 效果有限,应该考虑优化磁盘或调整 innodb_flush_log_at_trx_commit(前提是你能接受安全性的降级)。
- SHOW ENGINE INNODB STATUS 的 LOG 部分
看一下 Log sequence number(当前写入 log buffer 的 LSN)、Log flushed up to(已刷盘 LSN)、Last checkpoint at(最近检查点 LSN)之间的差距。
- 如果
Log flushed up to长时间与Log sequence number差距很大,说明刷盘跟不上写入; - 如果
Log flushed up to和Last checkpoint at差距大,说明检查点推进慢,问题可能在 buffer pool 脏页刷新而非 log buffer。
3.2 区分临时尖峰和持续瓶颈
任何参数调优都不能只看一两次快照。log buffer 的“够不够”要分场景,我总结三句话:
- 业务类型是大量小事务并发写:buffer 通常会周期性写满,即使每秒 redo 不大也会产生等待,这就是典型需要加大 buffer 的场景;
- 业务类型是少量大事务(ETL、批量导入):大事务会把 buffer 瞬时打满,但单事务的延迟瓶颈往往在磁盘 fsync,而不是 buffer 大小,加大 buffer 的效果要看 fsync 次数是否减少;
- 业务类型是读多写少:log buffer 通常长期处于低水位,16MB 完全够用,没必要改。
3.3 一种更直接的观测思路:看提交延迟拐点
如果手头已经有一个可以压测的环境,可以用 sysbench 的 oltp_write_only 跑不同 buffer 大小下的 TPS 对比。我自己在压测时常用一个更简化的方法:固定并发线程数(比如 64 线程),从 16MB 开始逐步调大 buffer,每档跑 5 分钟,记录平均 TPS 和 P99 延迟。
实际经验是:当 buffer 小于“高峰 1 秒内产生的 redo log 总量”时,TPS 会明显偏低,P99 延迟偏高;一旦 buffer 超过这个值两三倍,性能曲线会进入平台期,继续调大几乎没有收益。换句话说,log buffer size ≈ 高峰期每秒 redo 生成量 × 2~4 是一个相当实用的估算公式。
4. 参数修改完整实操:动态调整、验证与持久化
4.1 先确认当前值和支持的最小/最大范围
查当前值很简单:
sql复制SHOW VARIABLES LIKE 'innodb_log_buffer_size';
注意返回结果是字节单位,如果某个监控面板上显示的是 MB,别直接拿来做计算。
确认 MySQL 版本对动态修改的支持程度:
- MySQL 5.7.5 及以后、MySQL 8.0 全系列:支持
SET GLOBAL动态修改; - 更老版本:修改后必须重启生效。
用一句话给参数调优场景定基调:动态修改用于验证,配置文件持久化用于上线。
4.2 动态修改并验证生效的完整过程
建议在低峰期执行以下步骤,并打开一个监控窗口持续观察状态变量:
sql复制-- 第一步:动态修改
SET GLOBAL innodb_log_buffer_size = 64 * 1024 * 1024;
-- 第二步:确认修改生效
SHOW VARIABLES LIKE 'innodb_log_buffer_size';
-- 期望结果: 67108864
-- 第三步:观察状态变量是否有所改善
SHOW GLOBAL STATUS LIKE 'Innodb_log_waits';
SHOW GLOBAL STATUS LIKE 'Innodb_os_log_written';
注意几个细节:
-
动态修改后,已经存在的连接读取的变量有时会沿用旧值(取决于连接会话是否在修改前建立),但全局变量是立即生效的,新事务会使用新配置。为了保险,执行完
SET GLOBAL后建议重启一下相关业务连接池,或者直接从新连接确认。 -
动态修改只在内存中生效,MySQL 重启后会被重置。别调完参数就忘了写配置文件,否则下次重启就回滚了。
-
如果想在启动时固定,可以在
my.cnf的[mysqld]段添加:
ini复制[mysqld]
innodb_log_buffer_size = 64M
写 64M 还是写 67108864 都可以,MySQL 支持友好单位。不过如果用 Docker 或 Kubernetes 部署,建议通过环境变量或 ConfigMap 管理,避免直接在镜像里改配置文件。
4.3 调大后需要观察哪些指标判断是否有效
改完不是就完事了,需要至少观察一个业务周期(建议 24 小时,覆盖真实高峰):
- Innodb_log_waits 的增长速率:如果修改前每 10 分钟涨几千次,修改后应该明显下降;
- TPS / 提交延迟:看压测或线上监控的平均 TPS 是否提升,P99 延迟是否下降;
- 内存占用:log buffer 是 InnoDB 启动时就分配的内存段,调大后 RSS 会增加对应大小。在内存紧张的服务器上,不要盲目调到 256M,要预留 buffer pool 足够使用。
我用一个表格总结不同调整后的预期效果:
| 观察项 | Buffer 太小(典型症状) | 调到合理范围后 | Buffer 过大(副作用) |
|---|---|---|---|
| Innodb_log_waits | 持续增长 | 基本不增长 | 无明显变化 |
| 大事务提交耗时 | fsync 次数多,耗时长 | fsync 次数下降,耗时降低 | 无明显收益 |
| 实例内存占用 | — | 增加与设置值相当的量 | 白白占用内存 |
| 崩溃恢复时间 | — | 不影响 | 理论上可能略微延长恢复分析时间 |
4.4 一个完整的上线前验证清单
如果这是一次生产变更,建议按下面的清单走:
- 变更前记录基线:TPS、QPS、P99 延迟、Innodb_log_waits 值、redo log 生成速率;
- 变更时先动态调整并运行一段时间,观察是否有内存或性能异常;
- 确认无异常后写配置文件固化;
- 下次重启后在低峰期再确认
SHOW VARIABLES已读到新值; - 在变更总结里记录调整前后的性能曲线对比,方便后续判断是否需要进一步调整。
5. 别孤立地调这个参数:与周边参数的联动关系
5.1 innodb_flush_log_at_trx_commit:逻辑上最亲密的搭档
innodb_flush_log_at_trx_commit 决定每次事务提交时是否调用 fsync 把日志刷到磁盘:
- 值为 1(默认):每次事务提交都刷盘,安全性最高;
- 值为 2:每次事务提交只把日志写入操作系统的 page cache,不强制 fsync;
- 值为 0:事务提交时不主动刷盘,交给后台每 1 秒刷一次。
当这个值是 1 时,即使 log buffer 不够大,刷盘频率高,但事务提交本身可能等 fsync 的时间更明显;当值改成 0 或 2 时,提交操作很快返回,log buffer 的写满速度会明显加快,此时 innodb_log_buffer_size 是否足够会变得更重要。
注意:把 innodb_flush_log_at_trx_commit 调成非 1 会牺牲崩溃安全,允许丢失最近 1 秒左右的已提交事务,很多业务是不能接受这个前提的。我见过有人为了性能把这个参数改成 0,然后又因为 buffer 太小频繁触发刷盘,最后两头不讨好。调优要遵循一条顺序:先保证安全基线不变,再考虑其他性能手段。
5.2 innodb_log_file_size / innodb_log_files_in_group:磁盘上的“仓库容量”
redo log 文件的大小由 innodb_log_file_size 和 innodb_log_files_in_group 共同决定(8.0.30 之后又引入了 innodb_redo_log_capacity)。它们是磁盘上的日志仓库容量,和内存里的 log buffer 属于上下游关系。
如果磁盘上的 redo log 文件总容量太小,会频繁触发 checkpoint,强制把脏页刷盘,这时候就算 log buffer 再大,性能也上不去。所以遇到提交延迟升高的问题时,需要先排除:
- 是不是 redo log 总容量太小导致频繁 checkpoint?
- 是不是
innodb_max_dirty_pages_pct触发太早导致脏页刷新频繁? - 是不是磁盘 I/O 本身慢导致 fsync 耗时长?
log buffer 只在内存环节起作用,上游慢了加下游参数没意义。
5.3 MySQL 8.0.22 之后的 innodb_log_writer_threads
从 MySQL 8.0.22 开始,InnoDB 引入了独立的 log writer 线程,负责把 log buffer 里的数据刷入系统缓存,这减轻了用户线程的刷盘负担。如果你的版本在这个之后,调大 log buffer 的效果会与旧版本略有不同:用户线程写入 log buffer 的竞争降低了,但后台刷盘线程的处理能力同样受限于 redo log 生成速率和磁盘 I/O。
对于 8.0.22+ 版本,判断是否需要调大 log buffer 的核心依据依然是 log_waits 是否在增长,以及 performance_schema 里 log writer 线程相关等待事件的情况;如果已经看到 log_writer 线程的 wait/io/innodb/log_files 等待很高,说明磁盘成了瓶颈,加 buffer 不如处理磁盘延迟。
5.4 和 redo log 容量相关的 8.0.30 新变化
8.0.30 引入了 innodb_redo_log_capacity,用于统一替代 innodb_log_file_size 和 innodb_log_files_in_group,通过自动调整 redo log 文件数量,在 innodb_redo_log_capacity 设定的总容量下自动管理文件个数。此时如果你还在一行行配置旧参数去理解逻辑,可能会被误导。新版本里应该优先考虑 innodb_redo_log_capacity 设定的整体容量,再回头看 log buffer 是否够用。
6. 生产环境中实际调整的经验值和建议区间
6.1 不同场景下的推荐起点
我把常见场景的推荐初始值整理成一个表,注意这里的“不可盲目优化”是重点:
| 业务场景 | 推荐初始值 | 理由和重点观察项 |
|---|---|---|
| 读写均衡的常规 OLTP | 32MB ~ 64MB | 默认 16MB 在高峰期可能不够,64MB 提供充足余量,内存占用可忽略 |
| 大量短事务并发写(如秒杀、订单写入) | 64MB ~ 128MB | 短事务提交高频,容易触碰 buffer 写满和刷盘等待 |
| 周期性批量导入 / ETL | 128MB ~ 256MB | 大事务避免多次刷盘,但也要同时关注磁盘 fsync 能力 |
| 读多写少、低峰低 TPS | 16MB ~ 32MB | 几乎不需要调整,维持默认也可 |
强调一点:这些只是起点,不是万能预设值。我从一个 MySQL 源码维护者写的文章里看到过类似建议范围,但在实际生产里,真正的答案永远来自监控数据。
6.2 为什么我不建议直接调到 1GB
有些博客喜欢渲染“大 buffer 治百病”,给出的建议是“直接调到 256MB/512MB/1GB”。听起来很痛快,但有几个问题:
- log buffer 在实例生命周期内一直占用等量的内存,1GB buffer 意味着至少 1GB 内存被固定占用;
- buffer pool 通常占服务器内存的 60%~70%,再给它减去 1GB,留给操作系统 page cache 的空间就少了;
- buffer 调大并不会减少日志总量,只影响刷盘的分批方式,过量设置对性能几乎无增益。
我之前处理过一个“内存被神秘吃掉 1GB”的问题,最后发现是前任把 innodb_log_buffer_size 调到了 1GB,而业务实际产生的 redo log 每秒不到 500KB。清理掉这 1GB 后,系统整体性能反而稳定了。
6.3 结合实例配置给出一个合理的“先修改再验证”路线
我自己常用的方法是:
- 先用状态变量算出当前 redo log 峰值生成速率;
- 把
innodb_log_buffer_size设成“峰值速率 × 3”的估算值,并向上取整到 16MB 的整数倍; - 动态修改后观察 1~2 个业务高峰周期;
- 如果 log_waits 清零或达到平台期,则固化配置并保持观察;如果问题依旧,则说明瓶颈在磁盘 I/O 而不是 buffer;
- 记录每次变更前后性能数据,留档作为后续优化基线。
6.4 动态参数调整时的一个隐藏风险
SET GLOBAL 修改 log buffer 不是无痛的。InnoDB 在调整 log buffer 大小时需要重新分配内存,这期间会有短暂的锁等待。线上变更时最好放在低峰期,并提前确认 innodb_buffer_pool_size 是否预留足够内存。如果服务器内存本身很紧张,执行动态修改可能触发操作系统内存交换,反而造成更严重的性能问题。稳妥的做法是:测试环境先验证,生产环境低峰期执行,并保留快速回滚方案(改回默认值并重启)。
7. 一次真实故障排查的完整复盘
7.1 现象与初判
我接手过一个订单中心 MySQL 实例,高峰期出现周期性的“提交事务卡顿”。用户表现为写入订单偶尔要等好几秒才返回,数据库 CPU 和磁盘 I/O 都正常,buffer pool 命中率 99% 以上,怎么看都不像传统瓶颈。
第一轮排查时,我怀疑是锁等待。但查了 information_schema.innodb_trx 和 sys.innodb_lock_waits,没有发现长时间未提交事务。后来看了监控曲线,发现延迟抖动非常有规律,每隔几十秒一次。
7.2 定位过程:从状态变量到等待事件
接着我查了 SHOW ENGINE INNODB STATUS 中的 LOG 段落,注意到一个细节:Log sequence number 和 Log flushed up to 的差值在高峰时能达到几十 MB,而且后台日志写入线程经常处于“等待 log buffer 空间”的状态。随后拉取了过去一小时 Innodb_log_waits 的增量,发现增长速率惊人,每秒有几十次等待。
再深入 performance_schema:
sql复制SELECT EVENT_NAME, COUNT_STAR, SUM_TIMER_WAIT/1000000000 AS wait_ms
FROM performance_schema.events_waits_summary_global_by_event_name
WHERE EVENT_NAME LIKE '%innodb%log%'
ORDER BY SUM_TIMER_WAIT DESC LIMIT 10;
结果排在前面的有几个与 log_buffer 和 log files 相关的事件,但纯粹等待“buffer 空间”的占比很高。到这一步已经可以确认是 log buffer 偏小导致 Buffer 被写满,等待刷盘线程释放空间。
7.3 解决手段与验证结果
当时的 redo log 生成速率峰值约 10MB/s,默认 16MB 的 buffer 最多容纳 1~2 秒的写入量。我先把 buffer 动态调到 64MB,等待 5 分钟后再看指标:
- Innodb_log_waits 增长速率直接降了一半以上;
- P99 延迟从 180ms 降到 40ms;
- 但还没到理想状态,依然有少量等待。
于是我把 buffer 调到 128MB(等于大约 12 秒的 redo 生成量),这次 log_waits 基本归零,P99 延迟稳定在 20~30ms。最后我把 128MB 固化到配置文件,持续观察一周后确认问题彻底消失。
这个案例给了一个很直观的结论:当 log_waits 以稳定速率增长时,加大 buffer 是一种低风险、见效快的优化手段。
8. 大事务场景下的额外优化技巧与避坑提醒
8.1 大事务场景是先拆事务还是先调参数
对于单个事务就能生成几百 MB redo 的场景,调 log buffer 不是首选方案。比如一天一次的报表批量 UPDATE,几百 MB 的 redo log 即使 buffer 有 256MB,也可能需要多次刷盘,每次刷盘都伴随 fsync。更合理的做法是把大事务拆成多个小事务分批提交,每条事务控制在几十 MB 内,既能降低锁持有时间,也能减少 undo log 膨胀和 binlog 体积,对整体性能的改善比调 buffer 明显得多。
如果必须保留大事务(比如 LOAD DATA),那就是另一套优化思路,包括临时调大 innodb_log_file_size、调大 innodb_log_buffer_size、增大 innodb_buffer_pool_size、关闭双写缓冲(在从库或可重建环境)等。注意这些手段叠加时,要逐个验证效果,不要一锅端全改。
8.2 log buffer 相关的高频误区
- 误区一:调大 log buffer 能提高崩溃恢复速度。实际恰恰相反,log buffer 只是内存缓冲,崩溃后内存数据全部丢失,恢复要看磁盘上已有的 redo log 内容,和 buffer 大小没有直接正相关;
- 误区二:log buffer 满了才会刷盘。实际上 InnoDB 有专门的 log writer 线程每 1 秒左右会刷新一次,buffer 满只是触发刷盘的极端条件之一;
- 误区三:log buffer 设置得越大,落盘的 redo log 就越多。不对,redo log 的量由修改产生,和内存缓冲大小无关;
- 误区四:改了参数就必须重启。5.7.5 以后支持动态修改,可以利用这一点在低峰期进行无感验证,而不是每次调整都申请重启窗口。
8.3 几个值得长期监控的相关指标
最后补充一下生产环境里建议长期监控的指标清单:
Innodb_log_waits:log buffer 空间等待累计值,观察增长速率而非绝对值;Innodb_os_log_written:累计写入 redo log 的字节数,用于计算 redo 生成速率;Innodb_os_log_fsyncs:累计 fsync 次数,反映刷盘频率;- performance_schema 里的 log writer 相关等待事件(8.0 以上);
SHOW ENGINE INNODB STATUS中 LOG 段落三个 LSN 的差值。
顺带说一句个人习惯:我不会只在出问题时才查这些指标,而是把它们纳入日常巡检脚本,每天定时采集一次并记录趋势。这样下次再遇到提交延迟时,能立刻对比出“这次和上次的 log_waits 增长曲线有什么不同”,节省大量定位时间。这也是我做 MySQL 运维这些年效率最高的一种工作方式。
