MySQL 事务的实现机制:redo log 与 undo log 详解
MySQL 事务的 ACID 特性(原子性、一致性、隔离性、持久性)依赖于底层的日志机制和锁机制。其中,redo log(重做日志) 和 undo log(回滚日志) 是保障事务持久性和原子性的核心组件,配合 “日志先行(Write-Ahead Logging, WAL)” 策略,实现了高效且可靠的事务处理。
事务实现的核心思想:日志先行(WAL)
MySQL 执行事务时,采用 “先写日志,再写数据” 的策略(WAL),即:
- 事务修改数据前,先将修改操作记录到日志中;
- 确保日志写入磁盘后,再异步将数据更新到磁盘中的数据文件(如
.ibd)。
这一策略的优势在于:
- 日志文件是顺序写入的,而数据文件的修改可能是随机的(效率更高);
- 即使系统崩溃,未写入数据文件的修改可通过日志恢复,保证事务持久性。
redo log:保障事务的持久性
redo log 的作用
redo log 记录了事务对数据页的修改操作(如 “将 id=1 的行的 name 字段从 ‘A’ 改为 ‘B’”),用于在系统崩溃后恢复未写入数据文件的已提交事务,确保已提交的事务不会丢失(持久性)。
redo log 的写入流程
redo log 的写入涉及三个关键区域:
- redo log buffer(内存缓冲区):
事务执行过程中,修改操作会先记录到内存中的 redo log buffer(速度快,无需磁盘 IO)。 - os buffer(操作系统缓冲区):
当事务提交或满足一定条件时,redo log buffer 中的日志会被复制到操作系统的缓冲区(os buffer)。 - redo log file(磁盘日志文件):
最终通过fsync()系统调用将 os buffer 中的日志写入磁盘上的 redo log 文件(默认名为ib_logfile0、ib_logfile1)。
刷盘策略:innodb_flush_log_at_trx_commit 参数
该参数控制事务提交时 redo log 的刷盘时机,直接影响安全性和性能:
| 参数值 | 刷盘策略 | 安全性(数据可靠性) | 性能 | 适用场景 |
|---|---|---|---|---|
| 0 | 延迟写:事务提交时不刷盘,每秒将 redo log buffer 写入 os buffer 并调用 fsync()。 |
低(崩溃可能丢失 1 秒数据) | 最好 | 对数据安全性要求低的场景(如日志收集) |
| 1 | 实时写 + 实时刷:事务提交时,立即将 redo log buffer 写入 os buffer 并调用 fsync()。 |
高(无数据丢失) | 最差 | 核心业务(如金融交易) |
| 2 | 实时写 + 延迟刷:事务提交时写入 os buffer,但每秒调用 fsync() 刷盘一次。 |
中(崩溃可能丢失 os buffer 中的数据) | 较好 | 大多数非核心业务 |
- 默认值为 1,确保事务持久性(ACID 中的 D),这是 MySQL 对事务安全的默认保证。
redo log 的其他特性
- 循环写入:redo log 文件大小固定(由
innodb_log_file_size和innodb_log_files_in_group控制),写满后覆盖旧日志(已写入数据文件的日志可被覆盖)。 - LSN 关联:每个 redo log 记录包含 LSN(Log Sequence Number,日志序列号),用于标记日志顺序和数据页的一致性(数据页的 LSN 需与日志 LSN 匹配)。
undo log:保障事务的原子性
undo log 的作用
undo log 记录了事务修改数据前的原始状态(如 “id=1 的行的 name 字段原本是 ‘A’”),用于:
- 事务回滚:当事务执行失败或调用
ROLLBACK时,通过 undo log 恢复数据到修改前的状态(保证原子性)。 - MVCC(多版本并发控制):为读取操作提供历史版本数据,实现 “读不加锁”,提升并发性能。
undo log 的写入与存储
- 写入时机:事务执行修改操作时,先记录 undo log(与 redo log 类似,先写入 buffer,再刷盘)。
- 存储位置:默认存储在系统表空间(
ibdata1),MySQL 5.6+ 支持独立 undo 表空间(通过innodb_undo_tablespaces配置),便于管理和回收空间。
undo log 的生命周期
- 活跃期:事务未提交时,undo log 处于 “活跃” 状态,不能被删除(需支持回滚或 MVCC 读取)。
- 过期回收:事务提交后,undo log 变为 “非活跃” 状态,当不再被 MVCC 读取(如没有事务需要访问该历史版本)时,由后台线程(
purge线程)回收。
redo log 与 undo log 的协同工作
事务的完整执行流程中,redo log 和 undo log 配合保障 ACID 特性:
- 事务开始:MySQL 为事务分配事务 ID,记录初始 LSN。
- 执行修改:
- 读取数据页到内存(buffer pool)。
- 生成 undo log(记录原始数据),写入 undo log buffer。
- 修改内存中的数据页,生成 redo log(记录修改操作),写入 redo log buffer。
- 事务提交:
- 根据
innodb_flush_log_at_trx_commit策略,将 redo log 刷入磁盘。 - 标记事务为 “已提交”,释放 undo log 的活跃状态。
- 根据
- 崩溃恢复:
- 重启时,通过 redo log 恢复所有已提交但未写入数据文件的修改。
- 通过 undo log 回滚所有未提交的事务(保证原子性)