Mysql学习笔记(binlog 和 redolog)

955 阅读5分钟

这两个分别是什么?

Binlog

Binlog是数据库Service层的逻辑日志。

先讨论一下数据库Service层和引擎层的概念:

  1. 平时,提供数据库连接,提供SQL执行环境解析SQL的服务属于Service层
  2. 而引擎层则负责真正对接操作系统的文件系统,将数据持久化在文件系统中。

redolog

redolog是innodb引擎自己的日志,这个日志是“物理的”,记录了数据Page的修改情况。它实际也是逻辑的。

  1. 为什么有了Binlog还有redolog? A: 如上所说,Binlog是mysql逻辑层的日志,而redolog是innodb引擎的日志。

  2. 为什么说redolog既是物理的,也是逻辑的 A: 我个人的理解,物理的是指相对于Service层的,以及基于Page记录的特性,而逻辑的是指relog记录的也是某一行的相对变动,而不是某个时间点的镜像。

  3. 为了性能,innodb使用内存缓存数据,但为了数据的一致性,又通过写磁盘的方式记录了redolog,这有何意义? A: 我个人的理解,写日志毕竟是连续的写入,并非随机写入,所以性能(特别是在机械硬盘)要比不使用内存缓存优秀。

redolog文件存储

磁盘的最小读写单位是Block

先介绍一下文件系统/物理磁盘中的Block:

图源来自参考链接中的Blog

每个Block是512字节(B)(实际各个操作系统文件系统的block大小是可以自定义的),其中前12个字节用来存储Block相关信息,最后4个字节用来存储校验信息,中间连续的

  • 比如第一个字节的第一位(bit)是flush flag,看名称是标记此Block是否正在被写入。
  • 比如datalength,redolog日志连续写在磁盘上的,所以应该除了最后一个block,其他的datalength都是写满的。

redolog文件层的组织形式

REDO日志文件,以ib_logfile0、ib_logfile1…命名。为了避免申请存储空间,log文件都是预先写好的,实际使用中把它们形成头尾相连的循环链表循环覆盖写入。

每个log文件的开头固定预留4个Block来记录一些额外的信息,其中第一个Block称为Header Block,之后的3个Block在0号文件上用来存储Checkpoint信息,而在其他文件上留空:

image.png

当前日志的实时位置/位移量的计算

redolog在Block中的组织形式

sn是在写入redolog时全局唯一的逻辑偏移量

可以理解为你在班级方阵中的位置

lsn是redolog真正的物理偏移,所以要算上每个block的头尾信息,和当前block的头信息长度,长度如介绍block所示意,都是常量,只需要算一下block数量:

constexpr inline lsn_t log_translate_sn_to_lsn(lsn_t sn) {
  return (sn / LOG_BLOCK_DATA_SIZE * OS_FILE_LOG_BLOCK_SIZE +
          sn % LOG_BLOCK_DATA_SIZE + LOG_BLOCK_HDR_SIZE);
}

lsn是理想情况下所有block按顺序存储的位移量,要获得当前日志指针在整个redolog文件块的物理偏移量,需要再加上每个文件的header等。

image.png

上面讲到每个log文件第一个block会存储一些信息,其中就包括了此log文件第一个存储日志的block的lsn,可以称之为current_file_lsn, 这样的话,使用当前lsn - current_file_lsn 可以获得当前日志的在文件中的相对偏移量(光标位置),再加上current_file_offset可以获得real_offset。 如果将首位相连的多个REDO文件看成一个大文件,那么这里的real_offset就是这个大文件中的偏移。

我倒是可以理解每个偏移量含义,但由于没有完整读过源码或者整个innodb的完整架构设计,所以也并不知道以上三个偏移量是干吗用的。

redolog的写入流程

  1. 根据用户SQL生成Redolog
  2. 写入InnoDB Log Buffer
  3. 写入操作系统的Page Cache

Redolog的产生

一次原子操作(事务)可能涉及多行操作,所以会产生多个Redolog,为了保证一个事务的所有Redolog在整个日志中连续,单个原子操作的Redolog会先通过min-transaction缓存,等全部生成再写入LogBuffer

写入Log Buffer

LogBuffer是支持并发写入的,因为在高并发场景下,如果加锁限制Buffer写入,会成为性能瓶颈。

支持并发的,又要保证每个原子操作的多个Redolog连续,那需要计算每个 min-transaction(mtr) 的长度,获得独享的buffer空间。

可以想象,这个buffer中,虽然mtr都是连续分配的,但由于支持多个mtr并发写入,必定有很多已经拿到buffer空间的mtr,实际并没有写入数据或者没有全写进buffer中,如下图所示:

image.png

这个图的上半部分其实并不是指log buffer数组,而是buffer的索引,每个元素存的是buffer中mtr的lsn和长度,下面会讲到为啥有这个索引。

写入Page Cache

写入操作系统的文件系统时,为了效率,肯定是写入log buffer中靠前的连续的部分,后面已经分配空间但没写完的mtr就忽略掉。

为了找到当前已经完整连续写完的buffer位置(即上图的buf_ready_for_write_lsn),需要使用上面所说的索引数组。

而这个索引数组(buf_link)其实是个循环使用的数组,sn对数组长度取模的结果作buf_link下标

flush disk

写入PageCache后有专门的线程log_flusher调用fsync,通知操作系统写入磁盘。

参考资料

数据库内核月报

庖丁解InnoDB之REDO LOG | CatKang的博客