MySQL日志:
本文系统介绍了 MySQL 中三种关键日志:Undo Log、Redo Log 和 Binlog,以及它们在事务处理中的作用与配合关系。
Undo Log(回滚日志):用于记录数据修改前的状态,是实现事务原子性和 **MVCC(多版本并发控制)**的关键。事务回滚和快照读都依赖 Undo Log。
Redo Log(重做日志):用于记录对数据页的物理更改,即使事务已提交但数据尚未写入磁盘,借助 Redo Log 也能在系统崩溃后恢复数据,保障事务持久性。
Binlog(二进制日志):记录事务操作的逻辑信息,主要用于主从复制和数据恢复。Binlog 的刷盘策略由 sync_binlog 控制,对系统性能和数据安全有重要影响。
undo log :回滚日志
增删改记录的时候,会生成一个undolog日志 记录改记录之前是什么样子
- 实现事务回滚,保障事务的原子性。事务处理过程中,如果出现了错误或者用户执 行了 ROLLBACK 语句,MySQL 可以利用 undo log 中的历史数据将数据恢复到事务开始之前的状态。
- 实现 MVCC(多版本并发控制)关键因素之一。MVCC 是通过 ReadView + undo log 实现的。undo log 为每条记录保存多份历史数据,MySQL 在执行快照读(普通 select 语句)的时候,会根据事务的 Read View 里的信息,顺着 undo log 的版本链找到满足其可见性的记录。
redo log :重做日志
让mysql有了崩溃时恢复的能力,保证事务的持久性
当我数据进行变更的时候,不会立刻刷盘,而是写到buffer pool缓冲区 为了防止崩溃的时候数据丢失,因此需要redolog 记录XX 表空间中的 YYY 数据页 ZZZ 偏移量的地方做了AAA 更新
当系统崩溃的时候,如果没有对脏页进行刷盘,此时就可以去读取redolog中的内容进行数据恢复
此外undolog写的时候是写入到buffer pool中的undo页面的,当该页面被修改之后也需要记录到redolog中
刷盘时机:
redolog 最终是要写入到磁盘的,并且有两个redolog文件,循环写. 使用 write pos 表示 redo log 当前记录写到的位置,用 checkpoint 表示当前要擦除的位置 当 write pos追上checkpoint时候redo log写满了,此时mysql就会被阻塞 将脏页中的数据进行刷盘,然后擦除redolog
1.事务提交时,写入redolog的缓冲区 ,由后台线程每隔1秒写入磁盘
2.事务提交时,立刻写入磁盘
3.事务提交时,写入操作系统缓存区,由操作系统后台线程每隔1秒写入磁盘
因此 1相当于mysql进程崩溃,可能会丢失,但是3是操作系统崩溃才会丢失(mysql崩溃不会丢失)
binlog : 二进制日志
主要用于主从同步和数据备份
当事务提交之后,记录
有三种格式
1.记录逻辑sql语句
2.记录row行格式
3.折中模式,自行选择是记录sql还是row行
刷盘时机
- 所有事务的 binlog 先写入 binlog 文件,但可能仅存于 OS 缓冲区,并未真正写入磁盘。
- 由
sync_binlog
控制刷盘时机。 - 刷盘(fsync)
sync_binlog = 1
时,每次事务提交都会fsync()
,保证事务的 binlog 立即落盘。sync_binlog = N (N>1)
时,每 N 个事务后再fsync()
,提高吞吐量但可能丢失最近 N 个事务的 binlog。
两阶段提交
在 MySQL 的 InnoDB 存储引擎中,当 binlog 开启 时,MySQL 需要维护 redo log(物理日志) 和 binlog(逻辑日志) 两种日志。由于它们的刷盘策略是独立的,因此可能出现数据不一致的问题。例如:
- 只记录 binlog,未记录 redo log:如果崩溃,主从复制的 binlog 可能已经同步到从库,而 redo log 丢失,导致主库回滚事务,而从库已执行,形成数据不一致。
- 只记录 redo log,未记录 binlog:如果崩溃,事务虽然在主库已提交,但 binlog 丢失,可能导致主从数据不一致或备库恢复失败。
为了解决这个问题,MySQL 采用了内部 XA 事务,通过 binlog 作为协调者,InnoDB 存储引擎作为参与者,使用 两阶段提交(2PC) 确保 redo log 和 binlog 之间的数据一致性。其流程如下:
第一阶段:Prepare 阶段
- InnoDB 先写 redo log(prepare 状态)
-
- 记录 脏页、undo log、事务 ID(XID)、prepare 状态。
- 持久化到磁盘(
innodb_flush_log_at_trx_commit=1
)。 - 但此时事务还不能提交,因为 binlog 还未落盘。
第二阶段:Commit 阶段
- 写 binlog 并刷盘
-
- 记录 完整的 SQL 变更和事务 ID(XID)。
- 持久化到磁盘(
sync_binlog=1
)。 - binlog 刷盘成功,即认为事务成功提交。
- 提交 redo log
-
- 将 redo log 的状态从 prepare 变为 commit。
- 但 commit 状态不需要立即刷盘,只需写入 page cache,稍后由后台线程持久化。
关键点
- binlog 刷盘成功才算事务提交,redo log 的 commit 状态可稍后落盘。
- 这样做的目的是确保 binlog 一旦写入,redo log 必定可提交,避免主从数据不一致。
崩溃恢复时的处理
在 MySQL 崩溃恢复时,InnoDB 会扫描 redo log:
- redo log 处于 prepare 状态,binlog 没刷盘(时刻 A 崩溃)
-
- redo log 持久化了,但 binlog 丢失。
- MySQL 发现 binlog 没有此 XID,说明事务未提交,执行回滚。
- redo log 处于 prepare 状态,binlog 已刷盘(时刻 B 崩溃)
-
- redo log 持久化了,binlog 也刷盘。
- MySQL 发现 binlog 存在此 XID,说明事务已提交,执行提交。
关键点
- binlog 作为提交事务的唯一依据:没有 binlog,redo log 即使刷盘也必须回滚。
- redo log 可提前刷盘,但 binlog 只有在事务提交时才能刷盘。