面试知识库
进阶

redo-log刷盘策略#

一句话答案#

redo log 落盘由 innodb_flush_log_at_trx_commit 控制(0=每秒刷、1=每次提交都刷盘、2=每次提交写 OS 缓存每秒刷盘),binlog 落盘由 sync_binlog 控制(0=由 OS 决定、1=每次提交都 fsync、N=攒 N 次再刷);两者都设 1 就是”双 1 配置”,保证宕机不丢已提交事务,是金融级最高可靠性,代价是每次提交两次 fsync 性能最低。

核心要点

innodb_flush_log_at_trx_commit(控制 redo log):

提交时行为宕机影响性能
0只写 redo log buffer,后台每秒写 page cache 并 fsyncMySQL 进程崩溃就丢最近 1 秒最快
1(默认)写 page cache 并立即 fsync 到磁盘不丢任何已提交事务最慢、最安全
2写到 OS page cache,不主动 fsync,靠后台每秒 fsyncMySQL 崩溃不丢(已在 OS),主机宕机/断电丢最近 1 秒折中

sync_binlog(控制 binlog):

行为风险
0写 page cache,由 OS 决定何时 fsync宕机可能丢多个事务的 binlog
1每次提交都 fsync binlog不丢,最安全
N累积 N 个事务再 fsync宕机最多丢 N 个事务

关键认识:

  • WAL(Write-Ahead Logging):先写日志再改数据页,提交时只需顺序写 redo log(顺序 IO 快),脏页可异步刷盘
  • 双 1 = innodb_flush_log_at_trx_commit=1 + sync_binlog=1:每次事务提交 2 次 fsync(redo prepare 阶段 + binlog),配合两阶段提交保证 crash-safe 且主从一致
  • 0 和 2 的区别:0 是 MySQL 崩溃就丢;2 是数据已交给 OS,只有主机断电才丢

组提交(双 1 下性能的救命稻草):

双 1 每个事务都要 fsync,单看很慢,但实际高并发下 InnoDB 用组提交把一批并发事务的 fsync 合并成一次,所以双 1 的吞吐没有想象中那么低。

  • 提交分 Flush / Sync / Commit 三阶段流水线,每阶段一个队列、第一个进入的事务当 leader 带头,后续 follower 挂队尾
  • Sync 阶段 leader 对整组 binlog 做一次 fsync,N 个事务摊一次 fsync——这是省 IO 的核心
  • binlog_order_commits=ON 保证 binlog 写入顺序 == redo 提交顺序,主从才一致
  • 可用 binlog_group_commit_sync_delay 故意等几微秒攒更大的批,用一点延迟换更高吞吐
面试回答(2分钟版)

InnoDB 用 WAL 机制,事务执行时数据页的修改先写 redo log,提交时只要把 redo log 持久化就算事务安全了,脏页可以慢慢异步刷,这样提交路径上是顺序 IO 很快。但”redo log 写到哪一层”由 innodb_flush_log_at_trx_commit 控制:设 0 是只写 redo log buffer,后台每秒才刷盘,MySQL 一崩就丢最近 1 秒;设 1 是默认值,每次提交都直接 fsync 到磁盘,一条已提交事务都不丢,最安全也最慢;设 2 是提交时写到操作系统的 page cache 但不主动 fsync,MySQL 进程崩溃不会丢(数据在 OS 手里),但主机断电会丢最近 1 秒,是性能和安全的折中。

binlog 那边对应的参数是 sync_binlog:0 由 OS 决定刷盘时机、1 每次提交都 fsync、N 是攒 N 个事务再刷。两个参数都设成 1 就是业界说的”双 1 配置”,意味着每次提交都要分别把 redo log 和 binlog 刷到磁盘,配合两阶段提交,既保证宕机不丢已提交事务,又保证 redo log 和 binlog 一致、主从不会错乱,是金融、订单这类不能丢数据场景的标配。代价是每次提交两次 fsync,写性能最低。如果是日志、埋点这种允许丢一两秒的场景,常把它们调成 0 或 2、sync_binlog 设成 100~1000 来换吞吐。

追问与易错

追问方向:

  • “双 1 为什么是两次 fsync,不是一次?”→ 两阶段提交里:写完 redo log 进入 prepare 要 fsync 一次(受 flush_log_at_trx_commit 控制),写完 binlog 再 fsync 一次(受 sync_binlog 控制),两次都落盘才能保证 crash 后能用两个日志对齐事务状态
  • “设 0 和设 2 宕机表现差在哪?”→ 0 是 redo 还在 MySQL 自己的 buffer 里,MySQL 进程崩溃就丢;2 是已经交给操作系统 page cache,MySQL 崩溃不丢,只有整台机器断电/宕机才丢。所以 2 比 0 安全
  • “性能瓶颈在哪,怎么缓解?”→ 瓶颈是每次提交的 fsync 系统调用。MySQL 用 组提交(group commit) 把多个并发事务的日志合并成一次 fsync 来摊薄开销;也可适当用 sync_binlog=N 牺牲一点安全换吞吐
  • “线上一般怎么配?”→ 核心交易库双 1;从库或可容忍少量丢失的库常用 flush_log_at_trx_commit=2 + sync_binlog=100 提升性能

易错点:

  • ❌ 把 flush_log_at_trx_commit=0 和 =2 当成一样——0 丢在 MySQL 崩溃,2 只丢在主机断电
  • ❌ 以为只调 redo log 参数就安全——binlog 的 sync_binlog 不设 1,主从复制仍可能丢 binlog 导致不一致
  • ❌ 忽略组提交——以为双 1 下每个事务都独立 fsync,实际上高并发时多个事务会合并刷盘