mysql事务和日志文件有什么关系_mysql redo undo原理说明

来源:这里教程网 时间:2026-02-28 20:50:54 作者:

事务提交时,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 位置),缺一不可。

相关推荐