【MySQL 三大日志深度解析】:redo log、undo log、binlog 作用与两阶段提交原理
🔥你好我是fengxin_rou这是我的个人主页fengxin_rou的主页
❄️欢迎查看我的专栏我的专栏
《Java后端学习》、《JAVASE基础》、《JUC并发》、《redis》、《JVM虚拟机》、《MYSQL》、《黑马点评》、《rabbitmq》、《JavaWeb+AI的talis学习系统》、《苍穹外卖》
前言
MYSQL已经学习过了事务和锁,今天就来讲一讲日志的相关知识,主要讲解了mysql的三大日志,以及其对应的作用,并且探究了mysql是如何做到数据持久化,遇到mysql重启时数据丢失怎么恢复,mysql在提交事务时redolog和binlog的两阶段提交原理
一、MySQL 三大日志核心作用
MySQL 有三种最核心的日志:
1. redo log(重做日志)
作用:是InnoDB的引擎层生成的日志,保证事务持久性,用于掉电等故障恢复
- 属于 InnoDB 引擎层日志
- 记录物理日志:某个数据页被修改了什么
- 固定大小、循环写
- 核心价值:事务提交不用立即刷盘,MySQL 重启后能把已提交事务 “重做” 回来
2. undo log(回滚日志)
作用:是InnoDB的引擎层生成的日志,保证事务原子性,事务回滚,支撑 MVCC
- 记录逻辑日志:数据修改前的样子
- 用于事务回滚、多版本并发控制
- 不参与崩溃恢复,负责 “可回滚、可快照”
3. binlog(归档日志)
作用:是一种二进制日志文件,是Servicer层日志,数据归档、主从复制、时间点恢复
- 属于 MySQL Server 层日志
- 记录逻辑变更:SQL 或行变化
- 追加写、可无限扩容
- 支撑主从同步、增量备份、数据回档
二、redo log 为什么能保证崩溃恢复?WAL 机制
1. 什么是 WAL?
WAL = Write-Ahead Logging(预写日志)核心思想:先写日志,再写磁盘。
如果每次事务提交都直接刷数据页:
- 随机 I/O 极慢
- 高并发下数据库直接卡死
WAL 做法:
- 事务修改内存页(Buffer Pool)
- 写入 redo log buffer
- 事务提交时,只需要把 redo log 刷入磁盘
- 数据页慢慢后台刷盘即可
只要 redo log 落盘成功,数据就永久安全。
2. redo log 崩溃恢复流程
- MySQL 运行中宕机
- 内存数据丢失
- 重启后 InnoDB 扫描 redo log
- 把已提交但未刷盘的数据重新 “重做” 到数据页
- 数据库恢复到崩溃前一致状态
这就是 redo log 最核心的价值:保证事务持久性 + 大幅提升写入性能。
3.Redo log的顺序写入:
redo log采用追加写入的方式,将redo日志记录追加到文件末尾,而不是随机写入。这样可以减少磁盘的随机I/O操作,提高写入性能,因为redo log是一个环形大小的缓冲区,那么在末尾时会从头开始写,覆盖已经刷盘、不再需要的旧记录(循环写)
4.Checkpoint机制
因为数据库会定期将内存中的数据刷新到磁盘,此时就会将最新的LSN(log sequence number)写入磁盘,LSN标志了当前redo log刷新磁盘的操作到哪一个位置了。这个 LSN 表示:此 LSN 之前的数据都已经安全落盘。
所以总体流程就是
- 数据页 = MySQL 真实落在磁盘上的数据文件(.ibd)
- 崩溃前:磁盘数据页的 LSN 一定 ≤ redo log 的 LSN
- 恢复时:从「较小的那个 LSN(数据页 LSN)」开始,重做 redo log 里更大、更新的部分
- 最终把内存没来得及刷盘的数据,重新补写到磁盘数据页里
三、binlog 是什么?statement /row/mixed 区别
binlog 是 MySQLServer 层的逻辑日志,Server层每进行一次数据操作就会生成一条binlog,等事务结束后,统一把所有binlog写入binlog文件,并且binlog不会记录select语句,只会记录修改表结构和数据的语句
binlog是追加写,一个文件写满后再新建一个文件继续写。
1. binlog 三大格式对比
(1)STATEMENT 模式,又称逻辑日志
- 每进行执行一条sql语句就记录SQL 语句原文,主从复制中 slave 端再根据 SQL 语句重现
- 优点:日志小、性能好
- 缺点:有动态函数问题,如uuid、now这样随时在变函数会导致主从节点不一致,MySQL 5.7.7 之后将默认格式改为 ROW 的主要原因。
(2)ROW 模式(企业推荐)(MySQL 5.7.7 及之后的默认格式)
- 记录行的修改前后数据,一条update语句修改了多少行数据,就记录多少行
- 优点:主从完全一致、安全、可精准恢复,不会有动态函数问题
- 缺点:日志体积稍大
- 现在互联网公司默认使用 ROW
(3)MIXED 模式
- 混合 STATEMENT 和 ROW
- 简单 SQL 用 STATEMENT
- 不安全 SQL 自动切 ROW
- 折中方案,逐步被 ROW 取代
结论:生产环境直接用 ROW 格式。
四、redo log 和 binlog 的核心区别
| 对比项 | redo log | binlog |
|---|---|---|
| 所属层级 | InnoDB 引擎层 | MySQL Server 层 |
| 日志类型 | 物理日志(数据页修改) | 逻辑日志(SQL / 行变化) |
| 写入方式 | 循环写(固定大小) | 追加写(无限文件) |
| 主要作用 | 崩溃恢复 | 主从复制、数据备份 |
| 事务保证 | 保证 ACID 持久性 | 不保证事务,只记录变更 |
| 生成时机 | 事务执行中持续写 | 事务提交时一次性写 |
一句话记忆:redo 管崩溃,bin 管复制。
五、事务提交:redo log + binlog 两阶段提交原理
1. 为什么需要两阶段提交?
如果没有两阶段提交:
- redo 写成功、binlog 没写 → 主库恢复了数据,从库没有
- binlog 写成功、redo 没写 → 从库有数据,主库没有 最终导致主从数据不一致。
两阶段提交就是为了:让 redo log 与 binlog 逻辑完全一致。
2. 两阶段提交完整流程
阶段 1:prepare 准备阶段
- InnoDB 将 redo log 写盘
- 标记事务为prepare 状态(未最终提交)
阶段 2:写入 binlog
- 执行器写 binlog
- 刷盘成功,才算事务真正成功
阶段 3:commit 提交阶段
- InnoDB 将 redo log 标记为commit 状态
- 事务完成
3. 崩溃恢复怎么判断?
- prepare 阶段:将 XID(内部 XA 事务的 ID) 写入到 redo log,同时将 redo log 对应的事务状态设置为 prepare,然后将 redo log 持久化到磁盘(innodb_flush_log_at_trx_commit = 1 的作用);
- commit 阶段:把 XID 写入到 binlog,然后将 binlog 持久化到磁盘(sync_binlog = 1 的作用),接着调用引擎的提交事务接口,将 redo log 状态设置为 commit,此时该状态并不需要持久化到磁盘,只需要 write 到文件系统的 page cache 中就够了,因为只要 binlog 写磁盘成功,就算 redo log 的状态还是 prepare 也没有关系,一样会被认为事务已经执行成功;
prepare阶段是XID写入redo log, commit阶段是XID写入binlog
- 如果 binlog 中没有当前内部 XA 事务的 XID,说明 redolog 完成刷盘,但是 binlog 还没有刷盘,则回滚事务。对应时刻 A 崩溃恢复的情况。
- 如果 binlog 中有当前内部 XA 事务的 XID,说明 redolog 和 binlog 都已经完成了刷盘,则提交事务。对应时刻 B 崩溃恢复的情况。
- 如果 binlog 已写入 + redo 是 commit (时刻B后)→ 提交
- 如果 binlog 已写入 + redo 是 prepare (时刻B前)→ 提交
- 如果 binlog 未写入(时刻A) → 回滚
即只要 binlog 已经写入了该事务的 XID(事务 ID),恢复时就一定会把事务提交,不管此时 redo log 是
prepare还是commit状态。
两阶段提交 = MySQL 分布式事务的基石。
六、UndoLog日志的作用是什么?
undo log是用于回滚的日志,在一个事务中记录sql语句执行前的数据,在失败之后就做sql语句的反操作来回退
- 在插入一条记录时,要把这条记录的主键值记下来,这样之后回滚时只需要把这个主键值对应的记录删掉就好了;
- 在删除一条记录时,要把这条记录中的内容都记下来,这样之后回滚时再把由这些内容组成的记录插入到表中就好了;
- 在更新一条记录时,要把被更新的列的旧值记下来,这样之后回滚时再把这些列更新为旧值就好了。
