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:产生该事件的服务器 IDEnd_log_pos:事件结束位置Info:事件描述信息
小技巧:CDC 工具(如 Canal)本质上就是模拟 MySQL 从库,向主库发送 COM_BINLOG_DUMP 命令,然后持续接收 Binlog 事件流。理解了这个原理,你就知道 CDC 工具是怎么「监听」数据库变更的了。
3.5 Binlog 核心结构图
下面这张图展示了 Binlog 的整体结构和工作流程。我建议你仔细看看,理解了这个图,后面学 CDC 会轻松很多。
3.6 本章小结
Binlog 是 MySQL 数据变更的「完整记录」。它有三种格式,但做 CDC 实时同步,ROW 格式是唯一正确的选择。
开启 Binlog 只需要修改几个配置参数,重启 MySQL 即可。查看和解析 Binlog 可以用 mysqlbinlog 命令行工具,也可以用 SHOW BINLOG EVENTS 在客户端里查看。
嗯,这一节的内容就到这儿。你理解了 Binlog 的原理和配置,后面学 Canal、Debezium 这些 CDC 工具时,就会觉得「哦,原来是这样」。