MySQL 日志原理

日志

mysql当中的日志一共用三种:

  • undo log : 回滚日志,由Innodb存储引擎实现。用于 事务回滚和MVCC,实现了事务当中的 原子性
  • redo log : 重做日志,由Innodb存储引擎实现,用于掉电等情况下的 故障恢复,实现了事务当中的 持久性
  • binlog : 归档日志,是 Server层 生成的日志,用于 数据备份和主从复制

    事务当中的 隔离性 是通过 MVCC多版本控制链 实现的。

接下来我们会了解三种日志是怎么工作的。

Undo Log

为了实现事务的回滚,即撤回到事务之前的状态,Innodb提出了 undo log

每当IDB于一条记录进行操作(增删改)时,会把回滚(撤回操作)需要的信息记录下来。

  • 对于插入,需要记下新记录的主键,回滚时删除。
  • 对于删除,需要记下记录的内容,回滚时重新插入。
  • 对于更新,需要记下旧值,回滚时改回去。

不同操作的undo log的格式不尽相同。以更新为例,undo log除了记录本身之外,还包括了修改事务id trx_id 和指针 roll_pointer 指向上一个版本,形成版本链。

也是因为上面的原因,undo log除了实现事务回滚之外,也可以用来实现 MVCC

对于 读提交可重复读 隔离级别的事务来说,其 快照读(普通Select) 是通过Read View + undo log来实现的。

四种隔离级别:读未提交、读提交、可重复读、串行化。后三个分别解决了 脏读、不可重复读、幻读 的问题。

  • 读提交 在每次select语句之前都创建了一个快照Read View,可以解决脏读,但是无法重复读。
  • 可重复读 是在事务开始的时候创建了一个Read View,这样重复读同一个Read View,结果不变。

MVCC通过Read View + undo log实现,undo log为每条记录保持多份历史数据,mysql在快照读时,会根据RV里面的信息,顺着UL的版本链找到可见的记录。

在事务提交后,undo log不会直接清除,因为部分版本链可能还有用。但是会被逐步清除(delete_bit)。

Redo Log

Undo Log保存了事务之前的数据库状态,而 redo log 则保存了事务 完成之后 的状态。

redo log是针对 做的操作记录,也正因此,即使事务在数据页写磁盘的时候出现了故障,重启之后,尽管内存当中的脏页丢失了,mysql依然可以根据保存好的redo log来恢复其应该有的状态。所以说,redo log实现了事务的 持久性

  • undo log存放在undo页当中。undo页与数据页一样,存放在Innodb向系统申请的一段内存空间当中,叫做Buffer Pool,等待持久化到磁盘。
  • redo log存放在 Redo log buffer
  • Buffer pool当中的页不会立即写入磁盘,而是先写日志,等待合适的时机写入磁盘。这叫做 WAL(Write-Ahead Logging)技术

可以看到,undo log的持久化依赖于redo log。那么redo log是怎么持久化的呢?

redo log由于在磁盘上顺序追加写,所以速度会比普通的页持久化更快。

redo log 可能会在以下时机写入磁盘redo log文件:

  • mysql关闭
  • 每隔1s
  • redo log buffer过半
  • 事务提交时

redo log文件和redo log buffer大小都是固定有限的。从实际的角度来看,一旦事务提交,redo log就没有意义了,可以清除。

因此,redo log采用了一种循环写的策略。

redo_update_fromxlcoding

如上图所示,write_pos和checkpoint都顺时针移动。wrtie_pos表示写入的位置,checkpoint表示要擦除的位置。

因此,图中红色部分表示空白可写区域,而蓝色部分表示待写脏页。一旦 write_pos == checkpoint,则表示没有可写区域,此时mysql阻塞,将脏页写入磁盘,然后清空此部分redo log。

注意,上面讲的是 脏页 写磁盘的过程,与 redo log 写磁盘的时机区分开。
脏页写磁盘还有一些其他的时机。redo log buffer满只是其中一种。

Binlog


MySQL 日志原理
https://max-wzm.github.io/2023/02/12/mysql/
作者
Max Wang
发布于
2023年2月12日
许可协议