MySQL之redo log与binlog用法及说明
作者:北绿蚁
读这篇文章前,可以先看看前文 MySQL——update 语句执行流程,以便对整体有个大概的了解。
一、重做日志(redo log)
InnoDB 存储引擎会使用重做日志文件恢复到掉电前的时刻,以此来保证数据的完整性。
redo log 是物理日志,记录的是“在某个数据页上做了什么修改”;
- 采用 WAL(Write Ahead Log)策略,事务提交时,先写重做日志再修改页;
- 每个 InnoDB 存储引擎至少有 1 个重做日志文件组(group),每个文件组下至少有 2 个重做日志文件(如默认的 ib_logfile0 和 ib_logfile1);
- 可以设置多个镜像日志组,将不同的文件组放在不同磁盘上,以提高重做日志的高可用性;
- 在日志组中每个重做日志文件大小一致,并以循环写入的方式运行;
表1 重做日志文件参数
| 重做日志文件参数 | 作用 | 大小 |
|---|---|---|
| innodb_log_file_size | 指定每个重做日志文件的大小 | 最大 512GB |
| innodb_log_files_in_group | 指定日志文件组中重做日志文件的数量 | 默认为 2 |
| innodb_mirrored_log_groups | 指定日志镜像文件组的数量 | 默认为 1,表示只有一个日志文件组,没有镜像 |
| innodb_log_group_home_dir | 指定日志文件组所在路径 | 默认 ./ 表示在 MySQL数据库的数据目录下 |
下图 1 以一个重做日志文件组中有四个重做日志文件为例,每个日志文件大小最大只能 512GB。
图1 redo log 文件结构图

- 重做日志文件设置过大 —— 恢复时间长。
- 重做日志文件设置过小 —— 可能导致一个事务的日志需要多次切换重做日志文件。且会导致频繁地发生 async checkpoint,导致性能的抖动。
重做日志文件组
在每个组的第一个redo log file中,前四个块记录特定部分(2KB),之后才开始记录 log block。除了第一个 redo log file 中会记录,log group 中的其他 log file 不会记录这2KB,但是却会腾出这 2KB 的空间。
图2 redo log 写入过程

图2 redo log 写入过程

重做日志缓冲(内存中)写入磁盘的重做日志文件时,按 512 字节(一个扇区大小),因为扇区是写入的最小单位,因此可以保证写入必定是成功的。因此在重做日志的写入过程中不需要有 doublewrite。
重做日志格式
在 InnoDB 存储引擎中,对于各种不同的操作有着不同的重做日志格式。到 InooDB 1.2.x 版本为止,总共定义了 51 种重做日志类型(例如下图3 insert 语句和 delete 语句写入 redo_log_body 内容是不一样的)。虽然各种重做日志类型不同,但是它们有着基本的格式,下表 2 显示了重做日志条目的结构。
表2 重做日志条目结构
| redo_log_type | space | page_no | redo_log_body |
|---|
图3 不同重做日志格式

可以从上表 2 中看到,重做日志由 4 部分组成。
表3 重做日志条目结构解释
| 组成部分 | 作用 | 大小 |
|---|---|---|
| redo_log_type | 表示重做日志类型 | 1 字节 |
| space | 表示表空间的 ID | 采用压缩方式,占用空间可能小于 4 字节 |
| page_no | 表示页的偏移量 | 采用压缩方式 |
| redo_log_body | 表示每个重做日志的数据部分 | 恢复时需要调用相应的函数进行解析 |
表空间ID + 页号 即可唯一定位需要进行修改的页。
重做日志块
redo log 是基于页的格式来记录的,innodb 的页大小默认是16KB(由 innodb_page_size变量控制),一个页中可以存放多个 redo log block 重做日志块(512B),
图4 重做日志块

redo log恢复
假如数据在 LSN:10 000 的时候宕机了,则恢复 LSN:10 000 到 LSN:13 000 范围的日志,因为此时 CP 的 LSN 为 10 000。
图5 重做日志数据恢复

刷盘时机
重做日志写入重做日志文件触发条件:
- 主线程中每秒会将重做日志缓冲写入磁盘的重做日文件中,不论事务是否提交。
- 参数
innodb_flush_log_at_trx_commit控制,表示在提交(commit)操作时,处理重做日志的方式。 - 当重做日志缓冲池剩余空间小于1/2时,重做日志刷新到重做日志文件。
引发数据库刷脏页(flush)过程的情况:
- InnoDB 的 redo log 写满时,系统会停止所有的更新操作,把 checkpoint 往前推进(如图1),redo log 留出空间继续写。
- 系统内存不足时,淘汰一些数据页,如果是脏页则先刷到磁盘。
- MySQL 认为系统 ”空闲“ 时,只要有机会就刷一点 ”脏页“。
- MySQL 正常关闭时,MySQL 会把内存的脏页都 flush 到磁盘上。
二、二进制日志(binlog)
binlog 是逻辑日志,记录的是这个语句的原始逻辑,比如“给ID=2这一行的c字段加1 ”。
其有以下三大模式。
参数设置: binlog_format
Statement,基于 SQL 语句复制(statement-based replication, SBR)(默认)
- 二进制文件记录的是日志的逻辑 SQL 语句。
优点:不需要记录每一条 SQL 语句与每行的数据变化,这样子 binlog 的日志也会比较少,减少了磁盘IO,提高性能。缺点:在某些情况下会导致 master-slave 中的数据不一致。
ROW,基于行复制(row-based replication, RBR)
- 不只是记录简单的 SQL 语句,而是记录表的更改情况。
优点:只需要记录被修改的
Mixed,混合模式复制(mixed-based replication, MBR)
三、redo log 与 binlog 的区别
表4 redo log 与 binlog 的区别
| 区别 | redo log | binlog |
|---|---|---|
| 所处位置不同 | InnoDB 引擎特有 | MySQL 的 server 层实现的,所有的引擎都可以使用 |
| 记录内容不同 | 记录关于每个页的更改物理情况,即该日志是物理日志 | 记录一个事务的具体操作内容,即该日志是逻辑日志 |
| 写入方式不同 | 空间固定会用完,循环写入 | 不会覆盖以前的日志,追加写入 |
| 写入时间不同 | 在事务进行过程中,不断有重做日志条目(redo entry)被写入重做日志文件中 | 仅在事务提交前进行提交,即只写磁盘一次,不论这时该事务多大 |
- redo log 是物理日志,记录的是“在某个数据页上做了什么修改”;
- binlog 是逻辑日志,记录的是这个语句的原始逻辑,比如“给ID=2这一行的c字段加1 ”。
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
