MySQL写入速度慢是很多业务系统上线后迟早会遇到的问题,尤其是订单、日志、埋点这类写入密集型场景,一旦并发上来,insert和update的响应时间明显变长,应用侧堆积大量请求。造成写入慢的原因有很多,比如索引设计不合理、锁等待、磁盘本身性能差等,但在InnoDB引擎里,有一个参数经常被忽视,却直接决定了每一次事务提交的开销,它就是innodb_flush_log_at_trx_commit。这个参数控制的是redo log(重做日志)的刷盘策略,改对它,写入吞吐量往往能翻倍甚至更高。

为什么每次提交都会变慢:redo log的刷盘机制
要理解这个参数的作用,得先明白InnoDB的写入流程。MySQL修改数据时并不是直接改磁盘上的数据文件,而是先修改内存中的缓冲池,同时把这次修改记录到redo log里。redo log写完后,事务才算提交成功。问题就出在这一步:redo log本身也是先写到操作系统缓冲区的,什么时候真正写到磁盘上,就是由innodb_flush_log_at_trx_commit决定的。
这个参数有三个取值:0、1、2。默认值是1,也就是最严格的方式。每次事务提交时,MySQL都会调用flush把日志从内存强制刷到磁盘,确保事务一旦提交成功,数据就一定落盘了,即使MySQL进程崩溃或者机器断电,已提交的事务也不会丢失。这种方式的代价是每一次提交都要等磁盘IO完成,对于机械硬盘来说,每秒能承受的提交次数可能只有一两百,SSD会好一些,但依然是写入路径上的主要开销。
如果把值改成2,事务提交时日志只会写到操作系统的缓冲区,由操作系统决定什么时候真正落盘,MySQL本身不再强制刷盘。这样每次提交就变成了纯内存操作,速度大幅提升。风险在于:如果MySQL进程挂了,操作系统还在,日志不会丢;但如果操作系统崩溃或者服务器断电,操作系统缓冲区里还没落盘的日志就没了,最多可能丢失1秒钟的事务。
如果改成0,提交时连写都不写,日志的写入完全交给后台线程,后台线程每秒钟刷一次日志到磁盘。这是速度最快的方式,但风险也最大,MySQL进程崩溃就可能丢掉最近1秒的事务数据,一般只适合测试环境或者完全能容忍丢数据的场景。
三个取值的对比与实际修改方法
先用一张表把三种取值的差异列清楚,方便对照选择:
| 取值 | 提交时行为 | 崩溃时的数据丢失风险 | 性能 |
|---|---|---|---|
| 0 | 不写日志,后台线程每秒刷盘 | MySQL崩溃丢约1秒事务 | 最快 |
| 1 | 每次提交都强制刷盘到日志文件 | 不丢失任何已提交事务 | 最慢 |
| 2 | 写日志到OS缓冲区,不强制刷盘 | 仅操作系统崩溃或断电时丢约1秒事务 |
查看当前设置很简单,登录MySQL后执行下面的语句就能看到:
SHOW VARIABLES LIKE 'innodb_flush_log_at_trx_commit'; -- 默认返回 1 -- 动态修改,立即生效,不需要重启MySQL SET GLOBAL innodb_flush_log_at_trx_commit = 2;
动态修改只对当前实例有效,重启后会恢复。如果想永久生效,需要编辑配置文件my.cnf(Linux下一般在/etc/my.cnf),在[mysqld]段落里加上配置:
[mysqld] innodb_flush_log_at_trx_commit = 2
改完保存后重启MySQL服务即可。需要注意,这个参数是全局的,改了之后影响所有数据库、所有事务,不能只对某个库生效。如果同一个实例里既有日志表这种可容忍丢失的业务,又有资金流水这种绝对不能丢的业务,就要谨慎了。常见的做法是把这两类业务拆到不同实例,日志实例用2,核心交易实例保持1。
配合sync_binlog使用及批量写入优化案例
实际调优时,innodb_flush_log_at_trx_commit很少单独调整,通常要和sync_binlog一起考虑。sync_binlog控制的是binlog的刷盘策略,默认值1表示每次提交都把binlog刷到磁盘,开销同样不小。这两个参数组合起来有几种典型方案:双1配置(都是1)最安全,适合核心交易库;双0或1和2的组合性能最好,适合日志类、统计类业务;还有一种折中是把sync_binlog设为100,表示每积累100个事务刷一次盘,性能和安全性各让一步。
-- 常见的性能优先组合 SET GLOBAL innodb_flush_log_at_trx_commit = 2; SET GLOBAL sync_binlog = 100; -- 恢复安全优先的双1配置 SET GLOBAL innodb_flush_log_at_trx_commit = 1; SET GLOBAL sync_binlog = 1;
再来看一个实际案例。某个日志采集系统,每秒写入约5000条记录,程序是逐条insert,每条insert自动提交一次。在默认配置下,TPS只有几百,大量请求排队等待磁盘刷盘。优化分两步:第一步是应用侧改造,把逐条插入改成批量插入,每500条拼成一条多值insert语句,用一个事务提交,这一步把事务次数从5000降到了10;第二步是把参数改成2。改造后同样的硬件,写入吞吐量提升了十几倍,CPU和磁盘IO都处于健康水平。
这个案例说明一个道理:参数调优的前提是写入方式本身合理。如果应用是逐条提交,即使把参数调成0,每秒上千次的提交次数依然会压垮磁盘。反过来,如果已经做了批量处理,事务次数很少,参数保持1的开销也完全能接受。所以在动手改参数之前,先检查一下业务的提交模式,很多时候批量改造带来的收益比调参数更大也更安全。
最后提醒一点,调完参数要做充分验证。可以用show global status like 'Innodb_os_log_fsyncs'观察每秒刷盘次数的变化,也可以用压测工具对比调整前后的TPS。任何牺牲安全性的优化都要和业务方确认可接受的数据丢失边界,毕竟参数可以随时改回来,但丢掉的数据找不回来了。
mysql写入慢innodb_flush_log_at_trx_commitredo log刷盘修改时间:2026-09-16 23:52:42