binlog 是 MySQL 最为重要的日志文件,MySQL 数据的备份恢复、主从复制都依赖 binlog,相比于其他日志文件,binlog 可以说是刚需了。
binlog 常见命令
为了对 binlog 有一个直观的认知,我们先来看几个命令。
首先,默认情况下 binlog 并不会开启,我们可以通过下面的 show variabkes like'log_bin'命令查看 binlog 的开启状态:
sql
MySQL> show variables like 'log_bin';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| log_bin | OFF |
+---------------+-------+
如果你需要开启 binlog,可以在 my.ini 配置文件中可以加入下面的配置来开启:
bash
log_bin=/home/MySQL/bin-log.log
开启 binlog 以后,我们就可以看看 binlog 的内容了。由于 binlog 本身是二进制文件,我们不能直接打开查看,但我们只关心 binlog 内容的意义而不是内容本身,因此我们可以用 MySQL 提供的如下命令来查看 binlog 内容:
ini
show binlog events;
在 MySQL 中,输入上述命令后,我们能看到如下所示的返回结果:
sql
MySQL> show binlog events;
+----------------+------+-------------+-----------+-------------+---------------
| Log_name | Pos | Event_type | Server_id | End_log_pos | Info
+----------------+------+-------------+-----------+-------------+---------------
| bin-log.000001 | 4 | Format_desc | 1 | 107 | Server ver: 5.7.29-log, Binlog ver: 4 |
| bin-log.000001 | 99 | Query | 1 | 175 | BEGIN
|
| bin-log.000001 | 166 | Intvar | 1 | 203 | INSERT_ID=9
|
| bin-log.000001 | 200 | Query | 1 | 315 |insert into user (age,name) values(1,"test") |
| bin-log.000001 | 331 | Xid | 1 | 342 | COMMIT /* xid=22 */ |
+----------------+------+-------------+-----------+-------------+---------------
在这个返回的结果中,最重要的是 Event_type 和 Info 这两个字段,其余字段不影响我们学习理解 binlog,就不赘述了。其中,Info 是命令的具体内容,可以看到 Info 里面是具体执行的 SQL 语句。而 Event_type 是事件类型,它的作用是记录我们对 MySQL 所有变更操作的类型。
目前 MySQL 有 30 多种事件类型,归类了你对 MySQL 的所有变更操作。这些操作日志的内容共同组成了我们的 binlog,其中 Event_type 是 binlog 日志中的一个字段,因此认识 Event_type 对理解 binlog 的内容非常重要,我们后面会挑选几个 Event_type 类型介绍。
通过上面的返回的结果,我们可以看到,每个指令、每个动作都是一行记录,而在 binlog 里每一行被称作一个事件。也就是说 binlog 是由一个又一个事件组成的。
binlog 文件格式
在讲解 binlog 事件之前,我们先看看 binlog 的文件格式分类。binlog 有 3 种格式类型,分别是 STATEMENT、ROW、MIXED:
- STATEMENT
STATEMENT 是 binlog 的默认格式,我们后面对 binlog 的分析也是基于 STATEMENT 格式进行的。这个格式下,每一条修改数据的 sql 都会被记录到 binlog 中,slave 端再根据 sql 语句重现。
但 STATEMENT 的缺点也很明显,比如你用了 uuid 或者 now 这些函数,你在主库上执行的结果并不是你在从库执行的结果,这种随时在变的函数会导致复制的数据不一致。
- ROW
在 ROW 模式下,我们只需要知道行数据最终被修改成什么样了,不会出现 STATEMENT 下动态函数的问题。但 ROW 的缺点是每行数据的变化结果都会被记录,比如执行批量 update 语句,更新多少行数据就会产生多少条记录,使 binlog 文件过大,而在 STATEMENT 格式下只会记录一个 update 语句而已。
- MIXED
MIXED 包含了 STATEMENT 和 ROW 模式,它会根据不同的情况自动使用 ROW 模式和 STATEMENT 模式,MIXED 除了包含 STATEMENT 和 ROW 特性之外,还有一些针对自身优化的特性,比如压缩方式和一些特殊标记等等。
binlog 事件
在前面我们说了,binlog 由若干个事件组成,其中开头的第一个事件叫 Format_description,中文翻译为格式描述事件,文件结尾的最后一个事件叫做 rotate,中文翻译为日志轮换事件。其中 Format_description 包含了 binlog 的服务器信息、文件状态的关键信息等。
如果 MySQL 服务关闭或者重启,那么 MySQL 进程会自动创建一个新的 binlog,同时写入一个新的 Format_description。简单来说 Format_description 就是一个文件头。rotate 则包含下一个 binlog 的文件信息,它由 MySQL 写完 binlog 后添加到 binlog 的末尾,注意 rotate 只有当 binlog 写完才会有,binlog 没有写完的情况下是没有 rotate 的。
binlog 的基础结构如下,中间黄色部分就是我们执行的每一个事件。除了 select,剩下所有操作基本都会被记录,binlog 不记录 select 是因为 select 不会产生变更。

binlog 对所有产生变化的操作做了分类,我们挑几类常见的介绍一下:
- QUERY
这是最常见的类型,执行更新语句时会生成此事件,包括:create,insert,update,delete 等等。比如我们手动执行一个插入语句,然后再使用上面提到的 show binlog events 看一下下面的 binlog 文件内容:
sql
insert into test values(1,'yafeng');
| bin-log.000001 | 412 | Query | 1 | 536 | insert into test values(1,'yafeng'); |
| bin-log.000001 | 520 | Xid | 1 | 563 | COMMIT /* xid=30 */
和前面的日志内容相比,我们可以看到,经过 insert 操作,binlog 文件中多了一个 Query 类型的记录。
- XID
事务提交时产生的事件,上面 insert 语句就是一个事务,因此除了 QUERY 记录还产生了一条 XID 记录。
- FORMAT_DESCRIPTION
我们在前面提到了 FORMAT_DESCRIPTION,它是 binlog 的文件头记录。
- ROTATE
当 binlog 写完后会另启一个新 binlog 来记录日志,旧的 binlog 文件末尾会追加这个事件,什么情况下 binlog 会写完呢?有 3 种情况,一个是手动执行 flush logs 命令,第二是重启 MySQL,第三是 binlog 文件大于 max_binlog_size 参数配置的大小。
- INTVAR
SQL 中使用了 AUTO_INCREMENT 的字段,就会产生这个事件。
- STOP
当 MySQL 停止时会生成此事件,是的,你没听错,连 MySQL 的停止也会被记录到 binlog。
- RAND
SQL 中包含随机数函数的语句将产生 RAND 事件。
以上几个事件是 STATEMENT 格式下常见的几个,还有一些事件工作在 ROW 和 MIXED 格式下。binlog 在记录每个事件的时候也使用了 header 和 body 的格式,即 header 存储元数据,body 存储具体执行的语句。当然这并不是很重要,因为计算机领域中从网络协议到虚拟机到操作系统几乎都采用了类似的设计。下面我们来介绍 binlog 的变种文件 relay log。
relay log
relay log 中文名是中继日志,在主从复制的时候会辅助 binlog 完成复制任务。relay log 有着和 binlog 类似的格式和结构,可以看作是 binlog 的亲兄弟。唯一不同的地方是 relay log 多了 master.info 和 relay-log.info 两个文件。
master.info 和 relay-log.info 的文件内容非常小,一般以 info 为后缀结尾的文件都不大,里面记录了文件的指针。其中 master.info 记录 I/O 线程读取 binlog 的实时位置指针,relay-log.info 记录了 SQL 线程读取 relay log 的实时文件指针。 I/O 线程和 SQL 线程可以看作是在从库上工作的两个流水线工人,I/O 线程负责原材料的运输,写入本地的 relay log 中,SQL 线程负责从 relay log 获取原材料并加工。
现在我们再来回顾一下主从复制的流程,实际上这个流程有很多隐藏细节,既然我们今天提到了 master.info 和 relay-log.info,我们就来深入讲一下这个流程:
-
首先主库收到客户端请求语句,在语句结束之前向 binlog 写入事件。
-
之后,从库连接到主库,主库的 dump 线程从 binlog 读取日志并发送到从库的 IO 线程。
-
I/O 线程从 master.info 读取到上一次写入的最后的位置。
-
I/O 线程写入日志到 relay log,更新 master.info 的最后位置。
-
SQL 线程从 relay-log.info 读取进上一次读取的位置,然后在数据库中执行 sql,执行完更新 relay-log.info 的最后位置。
总结
今天我们聊了 MySQL 的 binlog,binlog 是 MySQL 最重要的日志文件。同时,由于 binlog 本身是二进制的,所以它的结构很神秘。好在 MySQL 为我们提供了一些命令去查看它的内容。
通过今天的学习,我们也知道了 binlog 有 STATEMENT、ROW 和 MIXED 3 种模式,不同的格式下会有不同的事件,他们各有优缺点,MySQL 默认使用 STATEMENT 模式。
每个 binlog 文件都是由不同的事件组成的,几乎所有变更操作都是事件。MySQL 有 30 多种事件,其中我们介绍了 QUERY、XID、FORMAT_DESCRIPTION 等几个事件。binlog 有个孪生兄弟 relay log,这是从库从主库克隆过来的 binlog,它只是用在主从复制场景。relay log 有两个 info 文件 master.info 和 relay-log.info,其中 master.info 服务于从库的 I/O 线程,relay-log.info 服务于从库的 SQL 线程。结合这两个 info 文件,我们回顾了主从复制的完成过程,加深了印象。