redo log 是什么?


为了确保数据不丢失,MySQL引入了 redo log 机制。在事务提交之前,必须写一条 redo log,redo log 写入成功才表明事务提交成功。redo log 会用磁盘顺序读写的形式往磁盘写数据,所以速度也是非常快的。

redo log 写入成功后,但是 buffer pool 里面的内存页还没有刷回磁盘的时候,服务器宕机了,重启后,也会根据 redo log 进行内存页中的数据恢复。所以保证了数据不会丢失。

redo log 本质上记录了某个表空间的某个数据页的某个偏移量的地方,改了几个字节的值,具体的值是什么。格式大致为 “表空间号 + 数据页号 + 偏移量 + 修改几个字节的值 + 具体的值“。

更具体一些,redo log 根据修改字节的个数来区分类型,MLOG_1BYTE 类型就是修改了1个字节的值,MLOG_2BYTE 类型的日志指的就是修改了 2 个字节的值。还有4个字节,8个字节等。如果是大量字符对应的是 MLOG_WRITE_STRING 类型。所以,redo log 日志更规范一些的格式大概是这样:日志类型,表空间ID,数据页号,数据页中的偏移量,具体修改的数据。如果是 MLOG_WRITE_STRING 类型,还会多一个修改数据长度。

redo log 是如何写入磁盘的?


redo log 写入磁盘的时候,并不是逐条写入磁盘文件的,而是先写入一个 redo log block 数据结构中,然后再将 redo log block 写入磁盘文件中。先来分析一下 redo log block 。redo log block 由3个部分组成,分别是 header , body , trailer 。一共512个字节,结构大概如下:

header
12字节
块编号
4字节
已经写入数据长度
2字节
第一个日志分组偏移量
2字节
check point no
4字节
body 496字节
trailer 4字节
  • block no :块的唯一编号
  • data length :记录 block 写入了多少字节的数据
  • first record group :???
  • checkpoint no :???

redo log block 也有类似 buffer pool 这样的内存缓冲池的概念。MySQL 启动的时候,也会在内存中申请一片空间,这些空间叫做 redo log buffer ,包含着多个 redo log block 。redo log buffer 默认的大小为16M,可以通过参数 innodb_log_buffer_size 来调整大小。

redo log 会写入在 block 的 body 中。执行事务的时候,会产生多个 redo log ,这些 redo log 就会组成一组,这一组 redo log 会在别的地方暂存,然后执行完毕后,再把一组的 redo log 写入到 block 中。如果一组 redo log 太大,会存放在2个 block 中,如果比较小的话,1个 block 也会存在多个 redo log 组。

写 redo log 日志的时候,先往内存中的 redo log buffer 中的 block 写数据,达到某些条件后,就会将redo log buffer 中的 block 刷回磁盘。以下几种情况的时候,会将内存中的 block 刷回磁盘:

  1. 后台有一个线程,每秒将内存的 block 写入到磁盘文件中对应的 block。
  2. 写入的 redo log 的大小,超过 redo log buffer 容量的一半(默认8M),就会强制刷回磁盘。
  3. 一个事务提交的时候,必须将这个事务的 redo log 所在的 block 刷回磁盘文件。这样才可以保证数据不丢失。
  4. MySQL 关闭的时候,也会将 block 刷回磁盘。

值得注意的一点是,写入磁盘的时候,会先写入操作系统的缓存,即OS Cache ,然后再刷入磁盘。
什么时候将 OS Cache 刷入磁盘,MySQL 提供了参数 innodb_flush_log_at_trx_commit 让用户决定。

  • 0:表示事务提交后,不进行 fsync,而是由 master 每隔 1s 进行一次重 redo log 的 fysnc
  • 1:默认值,每次事务提交的时候同步进行 fsync
  • 2:写入 os cache 后,交给操作系统自己决定什么时候 fsync

redo log 磁盘文件的处理


redo log 不停的写入磁盘文件后,磁盘文件当然也不会是无限增加的。默认情况下,redo log 会写入一个目录中的文件里,这个目录可以通过 “show variables like ‘datadir’” 来查看,也可以通过 innodb_log_group_home_dir 这个参数来设置目录。

redo log 的磁盘文件默认是有2个,每个48M。可以通错参数 innodb_log_file_size 指定每个文件的大小,通过参数 innodb_log_files_in_group 指定日志文件的数量。默认的这两个文件分别为:ib_logfile0 和 ib_logfile1 。内存中的 bolck 往磁盘中写入的时候先写第1个,然后写满了以后再写第2个,如果第2个也写满了,就会继续写入第1个,覆盖第1个文件的原先的内容。按照这样的机制循环写入。