一句话定位:控制事务提交时 Redo Log 的刷盘策略,直接决定崩溃后是否丢数据,是 MySQL 数据持久性的最核心参数。

一、三种模式对比

graph TB A[客户端 COMMIT] --> B{innodb_flush_log_at_trx_commit=?} B -->|1| C[立即写入 OS Cache + fsync 落盘] C --> D[返回客户端 OK] D --> E[崩溃后: 数据100%不丢] B -->|2| F[写入 OS Cache, 每秒 fsync一次] F --> D F --> G[崩溃后: OS正常可能丢1秒数据] B -->|0| H[写入 Log Buffer, 每秒写+fsync一次] H --> D H --> I[崩溃后: MySQL宕机可能丢1秒数据]

二、参数值详解(决策表)

刷盘时机

持久性

性能

数据丢失风险

适用场景

1 (默认)

每次 commit 调用 fsync()

最高

最差

0(不丢)

金融、订单、支付(必选)

2

每次 commit 写 OS Cache, 每秒 fsync()

OS 崩溃丢 1 秒

互联网业务(推荐)

0

每秒写 OS Cache 并 fsync()

最低

最好

MySQL 宕机丢 1 秒

日志型、可接受丢失

性能差异 (SSD,TPS 基准):

  • 1:100%(基准)

  • 2115%(提升 15%)

  • 0120%(提升 20%)


三、工作原理深度解析

模式 1:金融级安全(fsync 每次提交)

// 伪代码
trx_commit() {
    write_redo_to_os_cache();  // 写入 OS Page Cache
    fsync(fildes);             // 强制刷盘,等待磁盘返回
    return OK;                 // 确保落盘后才返回客户端
}

guarantee :只要磁盘不坏,事务提交后一定不丢。

模式 2:性能与安全平衡(write 每次提交,fsync 每秒)

trx_commit() {
    write_redo_to_os_cache();  // 写入 OS Page Cache,立即返回
    return OK;                 // 不等待磁盘
}
// 后台线程每秒执行一次
bg_thread() {
    fsync_all_pending_logs();  // 批量刷盘
}

风险:MySQL 崩溃没事,OS 崩溃(断电)可能丢最后 1 秒的事务。

模式 0:纯性能(每秒写+刷盘)

trx_commit() {
    write_redo_to_log_buffer(); // 只写内存 Log Buffer
    return OK;                  // 超快返回
}
// 后台线程每秒执行一次
bg_thread() {
    write_buffer_to_os_cache(); // 从 Log Buffer 写入 OS
    fsync_all_pending_logs();   // 再刷盘
}

风险:MySQL 进程崩溃(kill -9)或 OS 崩溃都会丢 1 秒数据。


四、场景化配置建议

场景 1:金融、电商、支付(钱相关)

[mysqld]
innodb_flush_log_at_trx_commit = 1
sync_binlog = 1  # 必须配合 Binlog 刷盘
# 持久性:100% 不丢
# 性能:磁盘 IOPS 决定 TPS 上限

场景 2:社交、内容、日志(互联网)

[mysqld]
innodb_flush_log_at_trx_commit = 2
sync_binlog = 100  # Binlog 每 100 次刷盘
# 持久性:OS 崩溃可能丢 1 秒,可接受
# 性能:提升 15%

场景 3:监控、埋点、大数据(可丢)

[mysqld]
innodb_flush_log_at_trx_commit = 2
sync_binlog = 0  # Binlog 不主动刷盘
# 持久性:可能丢 1 秒,完全可接受
# 性能:最大化吞吐

场景 4:批量导入(ETL)

-- 临时会话级调整(SET SESSION)
SET SESSION innodb_flush_log_at_trx_commit = 2;
SET SESSION sync_binlog = 1000;

-- 执行批量 INSERT
LOAD DATA INFILE 'data.csv' INTO TABLE t;

-- 恢复全局配置
SET GLOBAL innodb_flush_log_at_trx_commit = 1;

五、与 sync_binlog 的黄金组合

组合

含义

持久性

性能

推荐场景

1 + 1

Redo 和 Binlog 每次提交都 fsync

100%

最差

金融(必选)

2 + 100

Redo 写 OS,Binlog 批量刷

可能丢 1 秒

互联网(推荐)

0 + 0

两者都每秒刷

可能丢 1 秒

最好

可丢数据(监控)


六、监控与验证

1. 查看当前配置

SHOW VARIABLES LIKE 'innodb_flush_log_at_trx_commit';
SHOW VARIABLES LIKE 'sync_binlog';

2. 监控 I/O 等待

-- 查看 fsync 调用次数
SHOW STATUS LIKE 'Innodb_os_log_fsyncs';

-- 查看平均 fsync 耗时(微秒)
SHOW STATUS LIKE 'Innodb_os_log_pending_fsyncs';

3. 查看 TPS 与 IOPS 关系

# 命令行监控
iostat -x 1 | grep nvme0n1
# 如果 %util 持续 > 80%,说明磁盘是瓶颈
# 此时调大 innodb_flush_log_at_trx_commit 无法提升 TPS

七、硬件影响(SSD vs HDD)

SSD / NVMe

[mysqld]
innodb_flush_log_at_trx_commit = 1  # 性能可接受
# SSD 的 fsync() 延迟 < 1ms,对 TPS 影响小

HDD

[mysqld]
# HDD 的 fsync() 延迟 5-10ms,严重限制 TPS
# 若 IOPS < 1000,被迫选择 2
innodb_flush_log_at_trx_commit = 2  # 性能妥协

SSD 是硬件前提:用 HDD 跑 =1 的 MySQL 是灾难。


八、MySQL 8.0 新特性:innodb_flush_log_at_timeout

[mysqld]
# 8.0+ 新增,控制 mode 0/2 的刷盘间隔
innodb_flush_log_at_timeout = 1  # 默认 1 秒,可调大至 10 秒
  • 作用:延长 fsync 间隔,进一步提升性能

  • 风险 :丢数据窗口扩大

  • 场景:极端性能要求,可接受 10 秒数据丢失


九、快速决策树

业务是否涉及钱? → 是 → =1 + sync_binlog=1
          ↓
          否 → 是否 SSD? → 否 → =2 + sync_binlog=100
                   ↓
                   是 → 是否可接受丢 1 秒? → 是 → =2 + sync_binlog=100
                                             ↓
                                             否 → =1 + sync_binlog=1

十、总结与一句话决策

核心记忆点

  1. =1 是不丢数据的唯一选择 ,金融场景没得商量

  2. =2 是互联网标配 ,性能与安全平衡

  3. =0 几乎不用 ,除非纯日志且可接受丢失

  4. 必须配合 sync_binlog,两者共同决定持久性

  5. SSD 是硬件前提,HDD 被迫用 =2

一句话

innodb_flush_log_at_trx_commit = 1 是你的数据生命线,调之前先问钱包能不能接受丢钱。