三大日志
binlog
二进制日志
Server层的日志,逻辑日志,用来数据备份和主从复制。
写入时机
在每完成一次更新操作之后,生成一条binlog
刷盘时机
什么时候刷盘由sync_binlog参数决定
- 0:不去强制要求,由系统自行判断何时写入磁盘;
- 1:每次事务提交后,都要将binlog写入磁盘;
- N:每N个事务,才会将binlog写入磁盘。
方案
STATEMENT:记录数据变更的SQL语句本身
- 好:日志文件体积小,一SQL记一次
- 坏:可能存在数据不一致,对于动态函数---NOW()UUID()LIMIT等依赖上下文,会导致主从数据不一致
ROW:记录数据最终被修改成什么样
- 好:数据一致性最高,绝对一致主从
坏:日志文件体积大,更新多少行就记多少
MIXED:折中判断
特点
追加写,写满一个文件,就创建一个新的文件继续写,不覆盖,存全量日志--->恢复,主从复制
注意的是只记录库、表结构改变和数据修改的日志
redolog
重做日志
存储引擎层面的,物理日志,用来保障事务的持久性和崩溃恢复
Mysql有Buffer Pool缓冲池,跟redis一样。如果在Buffer Pool中的页的修改还没持久化到磁盘中,这个时候宕机了,事务更改就消失了
引入WAL技术,就是先写到日志,再写到磁盘上
写入时机
- 当执行一条更新语句(如
UPDATE)时,先在 Buffer Pool 中查找对应的数据页。如果不在,就从磁盘加载到 Buffer Pool。 - 接着,修改 Buffer Pool 中的这个数据页。此时,这个被修改了但还没刷回磁盘的数据页,就是所谓的 “脏页”。
- 标记脏页的同时,InnoDB把这个页面做了什么物理修改记录记录到Redo Log Buffer中
- 每一次数据变更都会产生对应的redo log
例如:在表空间xx文件、第xx页、偏移量xx的位置写入了xxx数据
刷盘时机
事务提交时,先把 Redo Log Buffer 中对应的所有日志记录,强制刷盘,写到redolog中, 此时,现在已经算是被持久化了。(由参数调控)
- trx_commit = 1:每次事务提交时,都必须立刻将 redo log 从内存刷入磁盘。(默认)
- trx_commit = 0:不保证事务提交时刷盘,而是大约每秒钟由后台线程将 redo log 从内存刷入磁盘一次。不安全
- trx_commit = 2:每次事务提交时,只把 redo log 从内存写入到操作系统的页缓存(Page Cache),不直接写入磁盘文件。然后大约每秒钟,操作系统会把它的缓存统一刷到磁盘。 折中
因为即使此时Buffer Pool 中的脏页还没有写回数据库,崩溃的时候,也可以依靠Redo log来进行重做,恢复到事务最后一次提交的状态
这就是WAL思想,先写日志,后写数据
两阶段提交
不一致问题
保障binlog和redolog的数据一致性,不一致的问题:
先写 Redo Log,后写 Binlog,但在写 Binlog 前宕机:
- Redo Log 已经持久化,数据可以恢复。
- Binlog 没有记录。
- 后果: 主库数据是完整的,但从库(通过 Binlog 复制)会缺失这条数据,导致主从数据不一致。
先写 Binlog,后写 Redo Log,但在写 Redo Log 前宕机:
- Binlog 已经记录,从库会复制这条数据。
- Redo Log 没有完全持久化,崩溃恢复时主库会回滚这条数据(WAL 思想失败)。
- 后果: 从库多出一条数据,主从数据不一致。
阶段一:Prepare 准备阶段
- 数据变更: 事务执行所有 SQL 语句,修改 Buffer Pool 中的数据页,并生成相应的 Redo Log 记录。
- Redo Log 刷盘:存储引擎将 Redo Log Buffer 中的数据刷入磁盘。
- 标记 XID: 在这条 Redo Log 记录上,会标记一个事务 ID (XID),并将其状态设置为 "Prepare"(准备状态)。
状态: Redo Log 已持久化到磁盘,但事务在 InnoDB 内部仍未最终提交。如果此时宕机,恢复时 Redo Log 会发现自己处于 "Prepare" 状态,等待 Binlog 的最终判断。
阶段二:Commit 提交阶段
Step 2.1:写入 Binlog(Server 层操作)
- 写入 Binlog:Server 层开始处理,将这个事务对应的 Binlog 事件(SQL 语句或行变更)写入到 Binlog 文件中。
- binlog刷盘: 此时,Binlog 也被强制刷盘。
- 记录 XID: 在 Binlog 事件中,同样会记录这个事务的 XID。
状态: Redo Log(Prepare 状态)和 Binlog 都已持久化到磁盘。Binlog 成为崩溃恢复的最终裁决者。
Step 2.2:写入 Redo Log Commit 标记(InnoDB 层操作)
- Redo Log 提交: 事务执行层通知 InnoDB 存储引擎,事务已完成 Binlog 写入。
- 最终标记: InnoDB 只需要在 Redo Log 上添加一个 "Commit" 标记(最终完成状态),表明该事务已彻底提交。这个操作通常只在内存中修改状态,并不强制刷盘。
状态: 事务彻底完成。数据修改对外部可见。
为什么 Step 2.2 不强制刷盘?
因为即使在 Step 2.2 标记完成前宕机,我们回到 Step 2.1 的状态:Redo Log (Prepare) + Binlog (已记录)。崩溃恢复时,MySQL 依然会根据 Binlog 来完成提交。所以,Step 2.2 的标记只是为了事务的正常结束流程,在崩溃恢复中不再是决定性的步骤。
崩溃恢复时恢复
2PC 的价值体现在崩溃恢复时。MySQL 重启时,会根据 Redo Log 和 Binlog 的状态,执行以下判断逻辑:
| Redo Log 状态 | Binlog 状态 | 恢复操作 |
|---|---|---|
| 未 Prepare | 无记录 | 回滚 :事务视为失败,Redo Log 没有任何记录,直接清理。 |
| Prepare 状态 | 有对应 XID 的记录 | 提交 :事务视为成功,强制完成 Redo Log 的 Commit 标记(以 Binlog 为准)。 |
| Prepare 状态 | 无对应 XID 的记录 | 回滚 :事务视为失败,因为 Binlog 缺失,所以从库无法复制,主库需要回滚。 |
| Committed 状态 | 有记录 | 跳过:事务已完成,正常运行。 |
undolog
回滚日志
逻辑日志,顾名思义,用来回滚的,也就是保障事务的原子性的作用。
记录修改之前的状态
Undo Log 的作用
作用一:保障事务的**原子性,回滚
实现机制:
- 当一个事务执行 ROLLBACK 命令,或者事务执行过程中遭遇异常中断(如死锁被终止、应用程序崩溃)时,系统会利用 Undo Log 记录的逻辑信息,将数据恢复到事务开始前的状态。
- 回滚流程: 沿着当前数据行上的 Roll_Pointer 指针,找到最新的 Undo Log 记录,执行其中的反向操作,从而撤销本次事务的所有修改。
作用二:实现多版本并发控制 (MVCC)
见事务篇
刷盘
在事务没提交之前,先把更新前的数据放到undo log 中,详细就是:
插入:记录插入的主键值就行--------回滚的时候只删除这个主键值对应的记录就行
删除:记录这个数据项的所有字段---回滚的时候把内容组成的记录重新插入到表中
更新:记录主键值和被修改的旧值---回滚的时候把列更新为旧值就行
随 Redo Log 刷盘而持久化。因为undolog本身就算是一个修改操作,修改了
异同
redo log 记录了此次事务「完成后」的数据状态,记录的是更新之后的值(持久性)
undo log记录了此次事务「开始前」的数据状态,记录的是更新之前的值(原子性)

