MySQL学习(十)redo log 介绍

前言

我们将从缓冲池开始介绍 ,然后后面开始介绍了 redo log 的底层到底是如何记录物理日志的.

缓冲池

在介绍 redo log 之前我们将会先介绍缓冲池 ,方便后续知道 redo log 的动机 .
InnoDB存储引擎是基于磁盘存储的 ,并将其中的记录按照页的方式进行管理 ,这一点有点像超级系统的 page cache ,我们之前也发过这张图片 ,是一个 SQL 语句的执行过程 ,其中可以看到是否命中内存就是命中数据库的记录是否在 page 中 .

图例

那么我们可以想一下 ,假如我们需要读取更改某个页, 就想操作系统一样 ,操作系统当发生缺页的时候 ,操作系统就需要等待数据加载到 page 之后才能进行操作 ,所以 MySQL 采用 WAL (Write Ahead Log) 的方式来加快写 ,先将SQL 操作写入到 redo log ,再修改 page , 也就是说redo log 和 page 里面的数据是最新的数据 ,而磁盘里的数据是最旧 ,那么什么时候把脏数据刷新到磁盘中去呢? MySQL 回刷的时间点称之为 : Checkpoint

MySQL学习(十)redo log 介绍

到了 checkpoint 就会将脏页回刷到磁盘 ,回刷的时机 :

(1) InnoDB 的 redo log 写满了,即是 write position 的位置追到了 checkpoint 的位置,这时候系统就会停止所有更新操作,把 checkpoint 往前推进,redo log 留出空间继续写。
(2) 系统内存不足,因为 MySQL 的数据都缓存在内存中,当系统的内存不足,那么就会有一部分数据会刷到磁盘中去
(3) MySQL 空闲的时候把数据进行刷盘
(4) 关闭数据库的时候,回刷数据回磁盘

而当要读入的数据页没有在内存的时候,就必须到缓冲池中申请一个数据页。这时候只能把最久不使用的数据页从内存中淘汰掉:

  • 如果要淘汰的是一个干净页,就直接释放出来复用;
  • 但如果是脏页呢,就必须将脏页先刷到磁盘,变成干净页后才能复用。

从上面我们可以看到redo log 的数据结构是一个环形的 ,这样 WAL 就变成了顺序写 ,提升了性能 .

redo 底层写了什么内容

下面图片和描述来自参考资料 ,非原创

redo 记录物理日志,记录的是“在某个数据页上做了什么修改”
例如下面的语句 :

update table set a = 1 where id = 1;

那么翻译成物理日志类似于这样 :

把第10表空间的第90号页面的偏移量为1024处的值更新为1

下面是大部分类型的redo log的通用结构:

MySQL学习(十)redo log 介绍

  • type:redo log的类型,目前redo log的类型很多
  • Space ID:表空间ID
  • page number:页号
  • data:一条redo log的内容

展示一下源码的数据结构的样子

struct alignas(INNOBASE_CACHE_LINE_SIZE) log_t {
    atomic_sn_t sn;                       // 目前log buffer申请的空间大小
    aligned_array_pointer<byte, OS_FILE_LOG_BLOCK_SIZE> buf;  // log buffer的内存区
    Link_buf<lsn_t> recent_written;               // 解决并发插入Redo Log Buffer后刷入ib_logfile存在空洞的问题
    Link_buf<lsn_t> recent_closed;        // 解决并发插入flush_list后确认checkpoint_lsn的问题
    atomic_lsn_t write_lsn;           // write_lsn之前的数据已经写入系统的Cache, 但不保证已经Flush
    atomic_lsn_t flushed_to_disk_lsn;         // 已经被flush到磁盘的数据
    size_t buf_size;                  // log buffer缓冲区的大小
    lsn_t available_for_checkpoint_lsn;      // 在此lsn之前的所有被添加到buffer pool的flush list的log数据已经被flsuh, 下一次checkpoint可以make在这个lsn. 与last_checkpoint_lsn的区别是该lsn尚未被真正的checkpoint.
    lsn_t requested_checkpoint_lsn;     // 下次需要进行checkpoint的lsn
    atomic_lsn_t last_checkpoint_lsn;       // 目前最新的checkpoint的lsn
    uint32_t write_ahead_buf_size;      // write ahead的Buffer大小
    lsn_t current_file_lsn;         // 
    uint64_t current_file_real_offset;      //
    uint64_t current_file_end_offset;       // 当前ib_logfile文件末尾的offset
    uint64_t file_size;             // 当前ib_logfile的文件大小
}

Redo log类型

先看一下 redo log 的基础类型
redo log类型主要是通过上面记录中的type体现的。比较基础的有以下几个(基础的类似于java里面的基本类型):

  • MLOG_1BYTE:type字段对应的十进制为1,表示在页面的某个偏移量处写入一个字节
  • MLOG_2BYTES:type字段对应的十进制为2,表示在页面的某个偏移量处写入两个字节
  • MLOG_4BYTES:type字段对应的十进制为4,表示在页面的某个偏移量处写入四个字节
  • MLOG_8BYTES:type字段对应的十进制为8,表示在页面的某个偏移量处写入八个字节
  • MLOG_WRITE_STRING::type字段对应的十进制为30,表示在页面的某个偏移量处写入一串数据

现在举一个例子。我们大部分情况下用的自增主键id都是int型或者是long型的,int为四个字节,long为八个字节,现在如果插入一条数据的话,这条数据实际是修改在buffer pool中的,然后通过redo log记录下当前的修改情况。那么这个时候,插入一条id(int)为9的数据的redo log应该是这样子的。

MySQL学习(十)redo log 介绍

插入数据后 , 含义: 在90表空间,编号为10页面,偏移量为1000处,写入四个字节,具体数据为0000 0000 0000 1001
其他类型的 redo log 这里不再深入 ,这里仅做抛砖引玉 ,方便大家理解 redo log 记录的内容是什么

其他

MySQL 的 master 线程执行事件

MySQL 的 master 线程执行事件如下 :
图例

问题

脏页 和 redo log 的关系 ?

当客户端第一次开始查询数据的时候,数据由于不存在 buffer pool ,那么数据会从磁盘加载数据到
buffer pool ,然后返回给客户端; 当客户端第二条语句是更改语句,那么MySQL 此时会

  • 更新内存中的数据
  • 写 redo log 日志
    这样就形成了脏页,那么脏页和磁盘中的干净页是不一致的,脏页需要flush回磁盘才能达到持久化,flush 的过程必定会导致
  • 脏页变成了干净页
  • checkpoint 向前一步推进

redo log 和 bin log 的区别

  • redo log是InnoDB引擎特有的;binlog是MySQL的Server层实现的,所有引擎都可以使用。

  • redo log是物理日志,记录的是“在某个数据页上做了什么修改”;binlog是逻辑日志,记录的是这个语句的原始逻辑,比如“给ID=2这一行的c字段加1 ”。

  • redo log是循环写的,空间固定会用完;binlog是可以追加写入的。“追加写”是指binlog文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。

小结

使用 redo log 是利用WAL(先写日志)技术,数据库将随机写转换成了顺序写,大大提升了数据库的性能。同事也可以保证事务的持久性, 保证事务持久性并不单单只有redo log,其实还有mysql的重要机制——double write,见这一篇文章

参考资料

上一篇:一生挚友redo log、binlog《死磕MySQL系列 二》


下一篇:log file sync等待事件