innodb_flush_log_at_trx_commit 控制 redo log 落盘时机而非是否持久化:0(每秒刷盘,可能丢1秒数据)、1(每次提交强制 fsync,强一致但慢)、2(提交仅写 page cache,os 崩溃可能丢1秒)。

直接说结论:innodb_flush_log_at_trx_commit 不是“要不要持久化”的开关,而是“**redo log 何时真正落盘**”的控制阀——它决定事务提交后,数据在崩溃时是否一定不丢,以及为此付出多少 I/O 代价。
为什么每次 COMMIT 都可能变慢?看懂 fsync 的真实开销
设为 1 时,每个 COMMIT 都会触发一次 fsync() 系统调用。这不是简单的“写内存”,而是强制让操作系统把日志从 page cache 刷到物理磁盘,并等待硬件确认完成。即使使用 NVMe SSD,单次 fsync() 延迟也常在 0.2–1ms;在 HDD 或高负载虚拟机上,可能飙到 5–20ms。高频小事务(如秒杀下单)下,这会成为明显瓶颈。
-
fsync()是阻塞的,事务必须等它返回才能结束 - OS 缓存(page cache)≠ 磁盘,
write()成功不代表数据安全 - 某些云厂商的块存储(如早期 AWS EBS gp2)或开启 write-back cache 的 RAID 卡,会让
fsync()表面快但实际不可靠——需配合hdparm -W0或厂商工具禁用磁盘缓存
值为 0、1、2 的行为差异远不止“刷不刷盘”
关键不是“有没有写文件”,而是“写到哪一步”和“谁负责落盘”:
-
0:事务提交完全不管日志;后台线程每秒把log buffer写入ib_logfile*并fsync()一次 → MySQL 崩溃 or OS 崩溃都可能丢最多 1 秒数据 -
1:每次COMMIT都write()+fsync()→ 数据真正落盘,ACID 完整,但性能最差 -
2:每次COMMIT只write()到 OS page cache,不fsync();OS 自行每秒刷盘(非严格保底)→ MySQL 崩溃不丢,OS 崩溃 or 断电可能丢最后 1 秒
注意:2 下的“每秒刷盘”依赖内核 pdflush 或 writeback 机制,若系统负载极高或内存压力大,实际刷盘可能延迟数秒。
别只看单参数,和 sync_binlog 搭配才决定最终一致性
innodb_flush_log_at_trx_commit 只管 redo log,而主从复制、闪回、PITR(时间点恢复)依赖的是 binlog。如果两者不同步,会出现“InnoDB 数据已提交,binlog 却没落盘”的裂口,导致:
- MySQL 崩溃重启后,事务看似成功,但 binlog 缺失 → 从库无法追平,CDC 流中断
- 误删后想用
mysqlbinlog回滚,却发现对应事件根本不在 binlog 文件里
因此生产环境常见组合:
- 强一致要求(金融/支付):
innodb_flush_log_at_trx_commit = 1+sync_binlog = 1(即“双1”) - 高吞吐 Web 应用(订单/评论):
innodb_flush_log_at_trx_commit = 2+sync_binlog = 1000(批量刷 binlog,降低频率) - 日志类写入(监控指标):
innodb_flush_log_at_trx_commit = 0+sync_binlog = 0(全靠 OS 调度)
动态修改要小心:生效范围与事务可见性
该参数支持 SET GLOBAL 动态修改,但有两点极易被忽略:
- 修改只影响后续新建立的连接,当前活跃事务和连接不受影响
- 修改后,老连接仍按旧值执行,直到断开重连 —— 所以改完必须确认应用连接池已重建
- 若配置在
my.cnf中,需重启 MySQL 才对所有连接生效;但线上一般不重启,优先走SET GLOBAL
验证是否生效,别只查变量:
SHOW VARIABLES LIKE 'innodb_flush_log_at_trx_commit';
更要观察实际行为:用 strace -p $(pidof mysqld) -e trace=fsync,write 抓取事务提交时的系统调用频次,比看配置更可靠。
真正难的不是选 0/1/2,而是清楚知道“我丢了这 1 秒数据,业务上能不能接受”。比如电商库存扣减失败可重试,但支付成功通知未发出,就可能引发客诉。这种边界,得和业务方一起画。











