3. MySQL Binlog 原理:从入门到实战

说实话,Binlog 是 CDC 系统的基石。没有它,后面所有实时同步都是空中楼阁。我刚开始接触 MySQL 时,对 Binlog 的理解也就停留在「备份用的日志」这个层面。直到后来做数据同步,才真正体会到它的威力。

这一节,咱们就把 Binlog 彻底讲透。从它是什么,到三种格式怎么选,再到怎么开启、怎么查看解析。你跟着走一遍,后面搭建 CDC 系统会顺很多。

3.1 Binlog 是什么?

Binlog,全称 Binary Log,二进制日志。它记录的是 MySQL 数据库所有数据变更操作——INSERT、UPDATE、DELETE,还有 DDL 语句比如 CREATE TABLE、ALTER TABLE 等等。

你想想看,数据库里每一条数据是怎么来的、怎么变的,Binlog 都记得清清楚楚。它就像飞机的黑匣子,记录了数据库的每一次「心跳」。

Binlog 的核心作用有三个:

  • 数据恢复:通过重放 Binlog,可以把数据库恢复到任意时间点。
  • 主从复制:从库读取主库的 Binlog,然后在自己这边执行一遍,实现数据同步。
  • CDC 数据捕获:Canal、Debezium 这些工具,本质上就是 Binlog 的「消费者」。

重要提醒:Binlog 只记录数据变更,不记录查询操作(SELECT、SHOW 等)。所以别指望从 Binlog 里查到谁查了哪些数据——它只管「写」,不管「读」。

3.2 Binlog 的三种格式

MySQL 提供了三种 Binlog 格式:STATEMENT、ROW、MIXED。我当年刚接触时,觉得这玩意儿随便选一个就行。结果踩了坑才知道,选错了格式,后面全是泪。

格式 记录内容 优点 缺点
STATEMENT 记录执行的 SQL 语句 日志量小,易于阅读 依赖上下文,可能产生数据不一致
ROW 记录每一行数据的变化 精确可靠,数据一致性高 日志量大,尤其大字段更新时
MIXED 混合模式,自动选择 兼顾两者优点 逻辑复杂,偶有意外行为

3.2.1 STATEMENT 格式

STATEMENT 格式,说白了就是「记 SQL」。比如你执行了 UPDATE user SET name='张三' WHERE id=1,Binlog 里就原样记下这条 SQL。

好处很明显——日志量小,一条 SQL 可能影响几千行,但日志里就一行记录。而且人眼直接能看懂,排查问题很方便。

但问题也出在这里。我遇到过这样一个场景:

-- 假设表里有 100 条数据
UPDATE user SET age = age + 1 WHERE id > 0;

这条 SQL 在 Binlog 里就一行。但如果主库和从库的数据排序不一样,或者用了 LIMIT 没加 ORDER BY,重放时结果可能不同。更麻烦的是,像 NOW()、UUID() 这些非确定性函数,每次执行结果都不一样。

避坑指南:我曾经在生产环境遇到过因为 STATEMENT 格式导致主从数据不一致的故障。从那以后,只要涉及数据同步的场景,我坚决不用 STATEMENT 格式。

3.2.2 ROW 格式

ROW 格式,记录的是「每一行数据怎么变的」。还是上面那条 UPDATE,ROW 格式会记录 100 行数据变更——每一行的旧值和新值。

举个例子:

# 假设有一条记录 id=1, name='张三', age=25
# 执行 UPDATE user SET age=26 WHERE id=1

# ROW 格式的 Binlog 会记录:
# 旧值: id=1, name='张三', age=25
# 新值: id=1, name='张三', age=26

这样做的好处是——精确。不管你怎么重放,结果都一样。CDC 工具(Canal、Debezium)最喜欢 ROW 格式,因为它们能拿到每一行的完整变更数据。

缺点也很明显——日志量大。如果一张表有 50 个字段,其中一个是 TEXT 大字段,更新一行可能产生几 KB 的日志。批量更新 10 万行,Binlog 文件瞬间膨胀。

我的建议:做 CDC 实时同步,必须使用 ROW 格式。这是行业最佳实践,没有之一。日志量大可以通过调整 Binlog 清理策略和磁盘空间来解决,但数据不一致的后果你承担不起。

3.2.3 MIXED 格式

MIXED 格式,是 MySQL 的「智能选择」模式。默认情况下用 STATEMENT 格式,遇到非确定性语句(比如用了 UUID()、NOW())时,自动切换成 ROW 格式。

听起来很完美对吧?但实际用起来,我总觉得有点「不放心」。因为你永远不知道 MySQL 在什么情况下会切换格式。而且 CDC 工具对 MIXED 格式的支持也不如 ROW 格式稳定。

我个人习惯:生产环境一律用 ROW 格式。MIXED 格式适合那些「既要日志量小,又要数据一致性」的折中场景,但 CDC 场景下不推荐。

3.3 Binlog 的开启与配置

Binlog 默认是关闭的。你需要修改 MySQL 配置文件来开启它。

3.3.1 开启 Binlog

编辑 MySQL 配置文件(通常是 /etc/my.cnf 或 /etc/mysql/my.cnf),在 [mysqld] 段添加以下配置:

[mysqld]
# 开启 Binlog
log_bin = /var/log/mysql/mysql-bin.log

# 设置 Binlog 格式为 ROW
binlog_format = ROW

# 设置 Binlog 保留天数
expire_logs_days = 7

# 设置单个 Binlog 文件大小上限
max_binlog_size = 1G

# 设置 Binlog 缓存大小
binlog_cache_size = 32K

# 设置 Binlog 行模式为 FULL(记录所有列)
binlog_row_image = FULL

配置说明:

  • log_bin:指定 Binlog 文件的路径和前缀。MySQL 会自动生成类似 mysql-bin.000001、mysql-bin.000002 的文件。
  • binlog_format:设置为 ROW,这是 CDC 的硬性要求。
  • expire_logs_days:Binlog 自动清理的天数。CDC 场景下,建议根据消费速度设置,一般 3-7 天。
  • max_binlog_size:单个 Binlog 文件达到这个大小后,会自动切换到下一个文件。
  • binlog_row_image:设置为 FULL,表示记录所有列的旧值和新值。CDC 工具需要完整的数据才能正常工作。

注意:修改配置后需要重启 MySQL 服务。重启前记得确认你的 CDC 工具已经消费完所有 Binlog,否则可能丢失数据。

3.3.2 验证 Binlog 是否开启

重启 MySQL 后,登录 MySQL 执行以下命令验证:

-- 查看 Binlog 是否开启
SHOW VARIABLES LIKE 'log_bin';

-- 查看 Binlog 格式
SHOW VARIABLES LIKE 'binlog_format';

-- 查看当前 Binlog 文件列表
SHOW BINARY LOGS;

如果 log_bin 显示为 ON,binlog_format 显示为 ROW,说明配置成功。

3.4 Binlog 的查看与解析

Binlog 是二进制文件,不能直接用 cat 或 vim 查看。MySQL 提供了两个官方工具:mysqlbinlog 和 SHOW BINLOG EVENTS。

3.4.1 使用 mysqlbinlog 查看

mysqlbinlog 是 MySQL 自带的命令行工具,可以把 Binlog 解析成可读的文本格式。

# 查看指定 Binlog 文件的内容
mysqlbinlog /var/log/mysql/mysql-bin.000001

# 查看指定时间范围内的 Binlog
mysqlbinlog --start-datetime="2024-01-01 00:00:00" --stop-datetime="2024-01-02 00:00:00" /var/log/mysql/mysql-bin.000001

# 查看指定位置范围内的 Binlog
mysqlbinlog --start-position=100 --stop-position=500 /var/log/mysql/mysql-bin.000001

# 输出为 SQL 格式(便于重放)
mysqlbinlog --base64-output=DECODE-ROWS -v /var/log/mysql/mysql-bin.000001

加上 -v 参数后,ROW 格式的 Binlog 会显示伪 SQL,方便阅读:

### UPDATE `test`.`user`
### WHERE
###   @1=1 /* INT meta=0 nullable=0 is_null=0 */
###   @2='张三' /* VARSTRING(100) meta=100 nullable=1 is_null=0 */
###   @3=25 /* INT meta=0 nullable=1 is_null=0 */
### SET
###   @1=1 /* INT meta=0 nullable=0 is_null=0 */
###   @2='张三' /* VARSTRING(100) meta=100 nullable=1 is_null=0 */
###   @3=26 /* INT meta=0 nullable=1 is_null=0 */

你看,每一列的旧值和新值都清清楚楚。这就是 CDC 工具能精确捕获数据变更的原因。

3.4.2 使用 SHOW BINLOG EVENTS 查看

如果你不想用命令行,也可以在 MySQL 客户端里直接查看:

-- 查看当前 Binlog 文件的事件列表
SHOW BINLOG EVENTS;

-- 查看指定 Binlog 文件的事件
SHOW BINLOG EVENTS IN 'mysql-bin.000001';

-- 从指定位置开始查看
SHOW BINLOG EVENTS IN 'mysql-bin.000001' FROM 100 LIMIT 10;

输出结果包含以下字段:

  • Log_name:Binlog 文件名
  • Pos:事件在文件中的起始位置
  • Event_type:事件类型(如 Query、Table_map、Write_rows、Update_rows、Delete_rows 等)
  • Server_id:产生该事件的服务器 ID
  • End_log_pos:事件结束位置
  • Info:事件描述信息

小技巧:CDC 工具(如 Canal)本质上就是模拟 MySQL 从库,向主库发送 COM_BINLOG_DUMP 命令,然后持续接收 Binlog 事件流。理解了这个原理,你就知道 CDC 工具是怎么「监听」数据库变更的了。

3.5 Binlog 核心结构图

下面这张图展示了 Binlog 的整体结构和工作流程。我建议你仔细看看,理解了这个图,后面学 CDC 会轻松很多。

Binlog 核心结构图 Binlog 核心结构图 MySQL 实例 数据变更操作 Binlog 文件组 mysql-bin.000001 mysql-bin.000002 ... Binlog 事件流 Table_map → Write_rows Update_rows → Delete_rows CDC 消费者 Canal / Debezium / Maxwell 三种 Binlog 格式对比 STATEMENT 格式 记录 SQL 语句 日志量小 可能数据不一致 不推荐 CDC 使用 ROW 格式 记录行变更(旧值+新值) 精确可靠 日志量大 CDC 首选格式 MIXED 格式 自动选择 STATEMENT/ROW 折中方案 逻辑复杂 CDC 场景不推荐

3.6 本章小结

Binlog 是 MySQL 数据变更的「完整记录」。它有三种格式,但做 CDC 实时同步,ROW 格式是唯一正确的选择。

开启 Binlog 只需要修改几个配置参数,重启 MySQL 即可。查看和解析 Binlog 可以用 mysqlbinlog 命令行工具,也可以用 SHOW BINLOG EVENTS 在客户端里查看。

嗯,这一节的内容就到这儿。你理解了 Binlog 的原理和配置,后面学 Canal、Debezium 这些 CDC 工具时,就会觉得「哦,原来是这样」。