mysql通过两阶段提交(2pc)协调redo log与binlog:prepare阶段写入并标记redo log为prepare;commit阶段写入binlog后通知innodb完成提交,确保崩溃恢复和主从一致。

事务提交时,redo log 和 binlog 怎么协同写入?
MySQL 用两阶段提交(2PC)确保事务在 redo log 和 binlog 中状态一致。否则主从同步或崩溃恢复时会出现数据不一致。
- 第一阶段(prepare):InnoDB 将事务的
redo log写入磁盘,并标记为PREPARE状态;此时事务尚未真正提交 - 第二阶段(commit):Server 层将该事务的完整逻辑操作写入
binlog文件;成功后通知 InnoDB 执行commit,redo log状态改为COMMIT - 如果 crash 发生在 prepare 后、binlog 写入前:重启后发现
redo log是 prepare 状态但无对应binlog,则回滚该事务 - 如果 crash 发生在 binlog 写入后、InnoDB commit 前:重启后发现有
binlog且redo log是 prepare 状态,则重放并 commit —— 这保证了主从复制和恢复的一致性
undo log 不是“redo 的反向”,它到底存什么、什么时候删?
undo log 存的是“逻辑前镜像”:比如 UPDATE t SET a=5 WHERE id=1,对应的 undo 就是一条 UPDATE t SET a=旧值 WHERE id=1,不是物理页还原。
- INSERT 产生的 undo 是 DELETE(逻辑删除该行)
- UPDATE/DELETE 产生的 undo 是反向 UPDATE(恢复字段旧值)
- 事务提交后,
undo log不会立即删除:MVCC 需要它提供历史版本,只有当所有活跃事务(包括长事务)都不再需要该版本时,purge线程才会清理 - 若存在运行超 1 小时的事务,它可能拖慢整个 undo 空间回收,导致
innodb_undo_log_truncate失效、ibdata1持续膨胀
为什么 redo log 要顺序写、固定大小、循环覆盖?
因为它是为 crash-safe 和高性能设计的物理日志,不是归档用途。
-
innodb_log_file_size×innodb_log_files_in_group决定了总大小(如 48MB),写满后从头覆盖 —— 这避免了随机写和文件增长开销 - 顺序追加写
ib_logfile0/ib_logfile1,比随机刷脏页快一个数量级;配合innodb_flush_log_at_trx_commit=1(默认)可保障每次 commit 都 fsync 到磁盘 - 但要注意:
innodb_log_file_size太小会导致频繁 checkpoint,加剧 I/O 压力;太大则 crash 恢复时间变长(需重放更多日志) - 修改该参数必须停库,且要先
mysqld --innodb-redo-log-init初始化新日志文件,否则启动失败
binlog 格式选 ROW 还是 STATEMENT?别只看文档说的“一致性”
生产环境几乎必须用 ROW,但代价真实:大事务可能让 binlog_cache_size 溢出到磁盘,拖慢提交速度。
-
STATEMENT在含NOW()、UUID()、@user_var等非确定函数时,主从执行结果必然不同 -
ROW记录每一行变更前后的完整镜像(含主键),主从严格一致,但单条 UPDATE 影响 10 万行 → binlog 写入 10 万条记录 - 若遇到大事务卡顿,先查
SHOW PROCESSLIST是否卡在Writing to net或Writing binlog;再确认binlog_cache_disk_use是否飙升 —— 是的话,临时调大binlog_cache_size(如从 32K→2M)可缓解 -
MIXED并不智能:它对某些语句仍强制用 STATEMENT,隐患难排查,不如明确选 ROW + 监控binlog日志体积增长速率
真正容易被忽略的点是:redo log 和 binlog 分属存储引擎层与 Server 层,它们的刷盘时机、缓存机制、错误处理完全独立 —— 所以不能靠其中一个推断另一个是否落盘成功。调试事务异常时,得同时查 show engine innodb status(看 redo 状态)和 show binary logs(看 binlog 位置),缺一不可。










