redo log 的应用
redo log 重做日志如何应用:
- 用户发起
update
事务语句,将磁盘数据页加载到内存(buffer_pool)缓冲区。 - 在内存中发生数据页修改,形成脏 页,更改中数据页的变化,记录到 redo buffer 中,加入对应字节大小的日志,更新 LSN。
- 当
commit
语句执行时,基于 WAL 机制,等到 redo buffer 中的日志完全落盘到ib_logfileN
中,commit
正式完成。 - 最终
ib_logfileN
中记录了一条日志,内容为某数据页发生了变化,生成了新的 LSN 号。
情景: 当 redo 日志被刷写到磁盘,但数据页依旧是脏页,此时宕机了。
- MySQL CR(自动故障恢复)工作模式,启动数据库时,自动检查 redo log 的 LSN 和原数据页 LSN。
- 如果发现
redoLSN > 原数据页的 LSN
,加载原始数据页+变化 redo log
到内存。使用 redo log 重构脏页(前滚)。 - 如果确认此次事务已经提交(
commit
标签),立即触发 CKPT 动作,将脏页刷写到磁盘上。
补充一点:
- MySQL 有一种机制,批量刷写 redo log 时。会在 A 事务 commit 时,顺便将 redo buffer 中的未提交的 redo 日志也一并刷到磁盘。
- 为了区分不同状态的 redo,日志记录时,会标记是否
COMMIT
(标签)。
redo log 主要保证了 ACID 中是 D(持久性) 的特性,另外 A(原子性)、C(一致性) 也有间接关联。
undo log 的应用
- 事务发生数据页修改之前,会申请一个 undo 事务操作,保存事务回滚日志(逆向操作的逻辑日志)。
- undo log 写完之后,事务会修改内存中数据页头部(会记录
DB_TRX_ID+DB_ROLL_PTR
),这个信息也会被记录的 redo log。
情景 1:当执行 rollback
命令时。根据数据页的 DB_TRX_ID+DB_ROLL_PTR
信息,找到 undo log,进行回滚。
情景2:事务未执行完毕宕机。
假设: undo log 有,redo log 没有。
- 启动数据库时,检查 redo log 和数据页的 LSN 号码。发现是一致的。
- 所以不需要进行 redo log 的前滚,此时也不需要回滚,undo 信息直接被标记为可覆盖状态。
假设:undo log 有,redo log也有(没有 commit
标签。)
MySQL CR(自动故障恢复)工作模式,启动数据库时,自动检查 redo log 的 LSN 和数据页 LSN。
如果发现
redoLSN > 数据页的LSN
,加载原始数据页+变化 redo log
到内存。使用 redo log 重构脏页(前滚)。如果确认此次事务没有
commit
标记,立即触发回滚操作,根据DB_TRX_ID+DB_ROLL_PTR
信息,找到undo log
,实现回滚。
以上流程被称之为 InnoDB 的核心特性:自动故障恢复(Crash Recovery)。先前滚再回滚,先应用 redo 再应用 undo。
undo log 在 ACID 中主要保证了主要保证事务的 A(原子性)的特性,同时 C(一致性) 和 I(隔离性) 的特性也有关系。
事务中的 C 特性怎么保证?
- InnoDB crash recovery:数据库意外宕机时刻,通过
redo 前滚 + undo回滚
保证数据的最终一致。 - InnoDB doublewrite buffer: 默认存储在 ibdataN 中,解决数据页写入不完整,DWB 一共 2M。分两次,每次 1 M写入。
mysqld process crash in the middle of a page write, InnoDB can find a good copy of the page from the doublewrite buffer during crash recovery.