Skip to main content

Redo log

基本概念

The redo log is a disk-based data structure used during crash recovery to correct data written by incomplete transactions

基于磁盘的数据结构,保证了崩溃恢复,保证已提交事务的恢复,并且通过WAL加快了写入的效率。

如何工作的

基于事务

  • 事务开始: 当事务启动时,MySQL会为该事务分配一个唯一的事务ID,同时在内存中为该事务分配一个Redo Log Buffer。
  • 数据修改:在事务执行过程中,当对数据进行修改(插入、更新、删除)时,这些修改操作会首先被写入到事务的Redo Log Buffer中。这是一个很快的操作,因为它涉及的是在内存中的缓冲区。
  • 持久化磁盘:一旦Redo Log Buffer满了,或者当事务提交时,MySQL会将Redo Log Buffer中的日志记录写入到Redo Log文件中, 这个过程是在修改实际数据之前,也就是WAL。然后更新write pos,当前写入的位置。
  • 更新Checkpoint:当Redo Log的记录被写入到数据页,更新Checkpoint,Checkpoint包括了LSN的信息,Checkpoint往前的都可以删了
  • 文件循环:当一个Redo Log文件写满或达到一定条件时,MySQL会切换到下一个Redo Log文件
tip
  • Redo Log Buffer:存储事务中的修改操作的内存缓冲区,用于事务的持久性和恢复,异步写入磁盘,和磁盘的数据页没有直接的关系。
  • InnoDB Buffer Pool:包含了表和索引的实际数据。从而减少了磁盘 I/O 操作,提高了数据读写的效率(刷新脏页...)。

崩溃恢复的流程

-- (todo:需要完善)

  • 通过redo log找到最近一次checkpoint的位置,然后根据checkpoint相对应的LSN开始,获取需要重做的日志。
  • 确定需要回滚和需要提交的事务id,XID(Transaction ID)
  • redo log回放,读取记录并应用到已提交的事务
    • 找到 XID: 从 prepare 记录中找到 XID,即事务标识符
    • 如果碰到既有prepare、又有commit的redo log,就直接提交
    • 如果碰到只有prepare、而没有commit的redo log,就拿着XID去binlog找对应的事务

特点

  • 顺序的磁盘写入,按照事务提交顺序写入(?有待加深理解)
  • WAL:Write-Ahead Logging
    • 在事务对数据库进行修改之前,先将修改的操作记录到一个持久的日志文件中
  • 崩溃恢复:恢复未持久化的内存数据

结构

  • Ordinary redo log files
    • 普通的redo log文件,正在使用的,使用#ib_redoN命名
  • Spare redo log files
    • 空闲的redo log文件,使用#ib_redoN_tmp命名
'#ib_redo582'  '#ib_redo590'  '#ib_redo598'      '#ib_redo606_tmp'
'#ib_redo583' '#ib_redo591' '#ib_redo599' '#ib_redo607_tmp'
'#ib_redo584' '#ib_redo592' '#ib_redo600' '#ib_redo608_tmp'
'#ib_redo585' '#ib_redo593' '#ib_redo601' '#ib_redo609_tmp'
'#ib_redo586' '#ib_redo594' '#ib_redo602' '#ib_redo610_tmp'
'#ib_redo587' '#ib_redo595' '#ib_redo603_tmp' '#ib_redo611_tmp'
'#ib_redo588' '#ib_redo596' '#ib_redo604_tmp' '#ib_redo612_tmp'
'#ib_redo589' '#ib_redo597' '#ib_redo605_tmp' '#ib_redo613_tmp'

优化

  • todo

Q&A

  • 为什么只用binlog不行
    • binlog不知道数据页刷入磁盘对应的点(redolog的checkpoint)
    • 如果恢复的话,只能恢复没有提交的数据,先回滚(undo log)再应用
  • 只用redo log的问题
    • 生态问题,很多中间件用到了binlog
    • 没法记录历史数据,只有内存的数据,起不到归档的作用

配置


# --- MySQL 8.0.30 or Higher ---
# redo log的磁盘空间,默认值: 104857600 字节 (100MB)。最大重做日志容量为128GB。
innodb_redo_log_capacity = 8589934592;
# --- Before MySQL 8.0.30 ---
# 每个日志文件的大小
innodb_log_file_size = 256M
# 日志文件的数量
innodb_log_files_in_group = 2

# redo log buffer的大小,默认值为 16MB,如果经常执行大的事务,那不妨增大这个值,这样就可以在事务提交之前避免写入磁盘
# The size in bytes of the buffer that InnoDB uses to write to the log files on disk. The default is 16MB. A large log buffer enables large transactions to run without the need to write the log to disk before the transactions commit.
# Thus, if you have transactions that update, insert, or delete many rows, making the log buffer larger saves disk I/O
innodb_log_buffer_size = 16MB
# 刷入磁盘的时机
# 1:每次事务提交时都会将日志写入并刷新到磁盘。(默认)
# 0:每秒将日志写入并刷新到磁盘一次。未刷新日志的事务可能会在崩溃中丢失。
# 2:日志会在每次事务提交后写入(把 redo log buffer 里的数据写入到 os buffer 中,但不立即执行fsync () ),并每秒刷新到磁盘一次。未刷新日志的事务可能会在崩溃中丢失。
innodb_flush_log_at_trx_commit = 1
# redo log的目录
innodb_log_group_home_dir =

查询

--redo log capacity resize mechanism的状态
SHOW STATUS LIKE 'Innodb_redo_log_resize_status';
+-------------------------------+-------+
| Variable_name | Value |
+-------------------------------+-------+
| Innodb_redo_log_resize_status | OK |
+-------------------------------+-------+
--redo log 容量限制
mysql> SHOW STATUS LIKE 'Innodb_redo_log_capacity_resized';
+----------------------------------+-----------+
| Variable_name | Value |
+----------------------------------+-----------+
| Innodb_redo_log_capacity_resized | 104857600 |
+----------------------------------+-----------+