MySQL binlog是记录数据库变更事件的二进制日志,仅STATEMENT格式可直接解析为原始可执行SQL;ROW格式需借助mysqlbinlog -v --base64-output=DECODE-ROWS等工具还原为伪SQL注释,不可直接执行;MIXED格式需先判别事件类型再分别处理。

MySQL binlog 是什么,哪些格式能直接解析成 SQL
MySQL 的二进制日志(binlog)本身不是纯文本 SQL,而是事件流(event-based),只有 ROW、STATEMENT、MIXED 三种格式。其中:只有 STATEMENT 格式下的事件能直接反解为原始 SQL;ROW 格式记录的是行变更前后镜像,必须借助工具(如 mysqlbinlog --base64-output=DECODE-ROWS -v)才能还原成类 SQL 的描述,但不是可执行的 DML。
常见误区:直接用 mysqlbinlog 输出管道给 mysql 执行,结果报错 ERROR 1062 (23000) at line X: Duplicate entry 或 Unknown table——因为 ROW 日志不含表结构、不保证幂等、也不含建表语句。
-
binlog_format = STATEMENT:安全前提下,mysqlbinlog输出可直接重放(但不推荐生产环境长期使用) -
binlog_format = ROW:必须解析后映射到目标库已存在的表,且需处理主键冲突、外键约束、自增 ID 跳变等问题 -
binlog_format = MIXED:混合行为,解析前得先确认具体事件类型,不可一概而论
用 mysqlbinlog 提取指定时间段/位置的增量事件
关键不是“全量导出”,而是精准截取迁移起点之后的变更。你需要两个锚点:start-position(或 start-datetime)和 stop-position(或 stop-datetime)。这些值通常来自全量备份时记录的 SHOW MASTER STATUS 输出。
示例:从 2024-05-10 14:20:00 开始,到 2024-05-10 14:25:00 结束,只提取 db1 库的事件:
mysqlbinlog \ --database=db1 \ --start-datetime="2024-05-10 14:20:00" \ --stop-datetime="2024-05-10 14:25:00" \ --base64-output=DECODE-ROWS -v \ /var/lib/mysql/mysql-bin.000012 > incremental.sql
-
--database是按USE db切换过滤,不是按表名;跨库操作会漏掉,慎用 -
--base64-output=DECODE-ROWS -v是解析ROW日志的必要组合,缺一不可 - 输出里含
# INSERT INTO `t1` VALUES这类注释行,不是真实 SQL,不能直接执行 - 若要生成可执行 SQL,需额外用
sed/awk清洗,或换用专用工具(见下一条)
生成真正可执行 SQL 的替代方案:go-mysql-transfer 或 binlog2sql
mysqlbinlog 原生命令无法把 ROW 事件转成带 INSERT/UPDATE/DELETE 的标准语句。binlog2sql(Python)和 go-mysql-transfer(Go)这类工具才做这件事:它们连接 MySQL 获取表结构元数据,再结合 ROW 事件中的列值、主键、before/after 镜像,拼出完整 SQL。
以 binlog2sql 为例,生成回滚/前滚 SQL:
python binlog2sql.py \ -h127.0.0.1 -P3306 -uadmin -p'xxx' -dtest -tstudent \ --start-file='mysql-bin.000012' \ --start-pos=12345 \ --stop-pos=67890 \ --flashback > rollback.sql
-
--flashback生成逆向语句(如把INSERT变成DELETE),不加则生成正向变更 SQL - 必须确保目标库存在对应表结构,工具不会自动建表或同步 schema
- 遇到
ALTER TABLE、DROP INDEX等 DDL,工具默认跳过;如需迁移 DDL,得单独抓取并人工校验 - 大事务(如单个
UPDATE影响百万行)会导致内存暴涨或超时,建议按 position 分段解析
增量迁移中容易被忽略的边界问题
很多人以为“解析出 SQL 就能直接导入”,实际在线上环境常卡在几个隐性环节:
- 时间戳字段(
TIMESTAMP/DATETIME)在ROW日志中是序列化为整数秒/微秒,工具还原时可能丢失精度或时区信息 -
JSON类型字段在ROW日志中以二进制 blob 存储,部分老版本binlog2sql会解析失败或乱码 - 源库启用了
GTID?那不能靠position定位,必须用--start-gtid参数,否则解析范围错位 - 目标库有触发器或外键约束?生成的 SQL 若未关掉
FOREIGN_KEY_CHECKS和AUTOCOMMIT,大概率报错中断 - 同一张表多个
UPDATE事件连续发生,工具按 event 顺序输出,但若中间有DELETE + INSERT模拟更新,还原出的 SQL 顺序可能与业务语义不符
最麻烦的其实是 DDL 同步——binlog 里的 Query_log_event 记录了原始 DDL,但工具往往不自动应用,你得自己从日志里 grep 出来,手动在目标库执行,并确认是否兼容(比如源库是 8.0,目标库是 5.7,JSON_TABLE 就跑不了)。


















