【MySQL修炼篇】一文讲透事务ACID背后的真正功臣:日志三剑客(Redo Log + Undo Log + Binlog)实战解析
MySQL 能实现事务的 ACID,99%的人都会背:
原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)
但真正扛起这四个特性的,是 InnoDB 的日志三剑客:
- Redo Log(重做日志)—— 负责持久性(Durability)和崩溃恢复
- Undo Log(回滚日志)—— 负责原子性和隔离性(MVCC)
- Binlog(归档日志)—— 负责主从复制和时间点恢复(属于Server层)
缺了任何一个,事务都站不住脚。
下面我们用最硬核的方式,一步步把它们拆穿给你看。
一、事务到底经历了什么?—— 经典更新语句全过程
update user set balance = balance - 100 where id = 1;
假设 balance 原值为 1000,事务开始后执行这条语句,InnoDB 内部到底干了什么?
- 先把 id=1 的行从磁盘读到内存(Buffer Pool),找到这一页
- 检查事务隔离级别(默认 REPEATABLE READ),生成 ReadView
- 在内存中把 balance 改成 900
- 生成 Undo Log(旧值 1000,用于回滚和MVCC)
- 生成 Redo Log(记录“把 balance 物理改成 900”)
- 把内存页标记为 Dirty Page
- 事务提交时:
- 写 Redo Log(刷到磁盘,fsync)→ 标记 prepare
- 写 Binlog(刷到磁盘,fsync)
- 再写 Redo Log(标记 commit)
→ 这就是传说中的两阶段提交(2PC)
只有这三步全部落盘,MySQL 才会返回客户端 OK!
二、日志三剑客深度拆解 + 实战配置
1. Redo Log(重做日志)—— 持久性的真正守护神
- 位置:默认 ib_logfile0、ib_logfile1(可通过 innodb_log_group_home_dir 修改)
- 大小:innodb_log_file_size(默认 48MB × 2,2024年后建议 1~4GB)
- 属于 InnoDB 引擎私有,物理日志(记录“在某个数据页上做了什么修改”)
核心参数实战推荐(8核32G机器,OLTP系统):
innodb_log_file_size = 2G # 越大越好,建议是 1小时redo日志量
innodb_log_files_in_group = 2
innodb_flush_log_at_trx_commit = 1 # 最安全,推荐生产使用
# 1 = 每次commit都fsync redo log(最慢最安全)
# 0 = 每秒fsync一次(快,有可能丢1秒数据)
# 2 = 每次commit写os cache,每秒fsync(推荐高并发场景)
为什么 Redo Log 能让 MySQL 这么快?
因为它实现了 WAL(Write-Ahead Logging):
先写日志 → 提交成功 → 后台慢慢刷脏页(Checkpoint)
没有 Redo Log,更新必须直接刷磁盘,性能直接崩。
实战查看当前 redo log 使用情况:
show engine innodb status\G
------------------------
LOG
------------------------
Log sequence number 23 1308424592 -- 当前lsn
Log flushed up to 23 1308424580 -- 已刷到磁盘的lsn
Last checkpoint at 23 1300000000 -- 上次checkpoint位置
# 如果 Log sequence number - Last checkpoint at 接近 innodb_log_file_size 总和
# 说明 redo log 快写满了,会触发同步刷脏页,性能下降!
2. Undo Log(回滚日志)—— 原子性 + MVCC 的幕后英雄
- 位置:默认在 ibdata1 系统表空间(5.7前),5.7+ 可独立 undo tablespace
- 逻辑日志:记录旧版本数据
用途两大:
- 事务回滚
- MVCC 多版本并发控制(可重复读的核心)
每条更新语句都会生成 undo log,保存旧值 + trx_id + roll_pointer
查看 undo log 使用情况:
select count(*) from information_schema.innodb_trx; -- 当前活跃事务
show variables like 'innodb_undo_tablespaces'; -- 推荐设为 3~16
实战:长事务会导致 undo log 暴涨,占用大量空间,甚至撑爆表空间!
-- 危险!千万别在生产这么干
begin;
select * from big_table where id=1;
-- 然后几个小时不提交......
-- 这段时间所有更新这个行的语句都会生成新版本,undo log 疯狂增长
3. Binlog(二进制日志)—— 主从复制的灵魂
- 属于 Server 层,所有引擎都有(MyISAM也有)
- 逻辑日志:记录 SQL 语句或行变化
- 格式三种:STATEMENT(SBR)、ROW(RBR,最常用)、MIXED
推荐配置(2025生产标配):
server_id = 1
log_bin = /data/mysql/binlog/mysql-bin
binlog_format = ROW # 必须是ROW!!
expire_logs_days = 14
max_binlog_size = 1G
sync_binlog = 1 # 最安全,和 redo log 一样每次commit fsync
# 如果不在乎丢最后几条binlog,可设为 1000+,配合 innodb_flush_log_at_trx_commit=2 刷爆QPS
binlog_rows_query_log_events = ON # 便于排查问题
三、崩溃恢复时,三剑客如何通力合作?
MySQL 崩溃重启后,InnoDB 会执行 Crash Recovery:
- 从 Redo Log 最后一次 Checkpoint 开始,重放所有 Redo Log(包括未刷盘的脏页操作)
- 对于已经 commit 但还没刷盘的事务:Redo Log 有 prepare + commit → 正常重做
- 对于已经写 binlog 但没 commit 的事务:Redo Log 只有 prepare → 会回滚(保证一致性)
- 对于写了 commit 但没写 binlog 的事务:重启后会自动提交(5.7+行为)
这就是“到底以谁为准”的终极答案:
redo log 的 commit 标记才是事务是否真正提交的最终标志!
四、生产级最佳实践总结(直接抄走)
# my.cnf 黄金配置(高并发OLTP系统,2025-2026推荐)
[ mysqld ]
innodb_buffer_pool_size = 70% 内存
innodb_log_file_size = 2G
innodb_log_files_in_group = 2
innodb_flush_log_at_trx_commit = 1
innodb_undo_tablespaces = 8
log_bin = /data/mysql/binlog/mysql-bin
binlog_format = ROW
sync_binlog = 1
binlog_rows_query_log_events = ON
server_id = 1337
# 监控必开
innodb_monitor_enable = all
performance_schema = ON
五、一句话总结
- Redo Log 保证了 D(Durability)—— 崩溃不丢数据
- Undo Log 保证了 A(Atomicity) + I(Isolation/MVCC)
- Binlog 保证了主从复制和最终一致性
- 两阶段提交 保证了 Redo Log 与 Binlog 的一致性 → 真正实现 C(Consistency)
没有这三把剑,事务就是空中楼阁。
重阳老哥,把这篇发给你的后端兄弟们,让他们彻底明白:
MySQL 的事务 ACID,从来不是天上掉下来的,而是这三份日志用命换来的!
看完这篇,下次再有人问你“为什么 innodb_flush_log_at_trx_commit=1 最安全”,你直接甩他这张图就行了:
Redo Log(prepare) → Binlog → Redo Log(commit)
这就是 MySQL 事务的命根子。