MySQL闪回前提为binlog开启且格式为ROW;需检查log_bin=ON和binlog_format=ROW,再用mysqlbinlog解析事务边界,提取Delete_rows_event生成反向INSERT,并关闭sql_log_bin执行恢复。

确认 MySQL 是否开启了 binlog 且格式为 ROW
闪回的前提不是“有没有 binlog”,而是 binlog 必须存在、且格式是 ROW。如果配置的是 STATEMENT 或 MIXED,绝大多数 DELETE/UPDATE 操作无法精确定位到被删的行——因为日志里只记了 SQL 语句,不记具体改了哪几行。
检查方式:
SHOW VARIABLES LIKE 'log_bin';<br>SHOW VARIABLES LIKE 'binlog_format';
常见错误现象:log_bin 是 OFF,或 binlog_format 是 STATEMENT,此时直接放弃闪回,考虑备份恢复。
- 线上库默认关 binlog 的情况不少见,尤其测试环境或老版本 MySQL
-
binlog_format在 MySQL 5.7+ 默认是ROW,但很多运维会为兼容性手动切回STATEMENT - 即使开了
ROW,也要确认误删时间点前的 binlog 文件没被PURGE或磁盘清理掉
定位误删操作在 binlog 中的位置和事件范围
不能靠猜,得用 mysqlbinlog 解析出具体事务起止点。重点不是“找到 DELETE 语句”,而是找到它所在的完整事务(BEGIN 到 COMMIT),因为 ROW 格式下,删除行为记录在 Delete_rows_event,而它必须结合前一个 Table_map_event 才能还原表结构和库名。
实操建议:
- 先用
mysqlbinlog --base64-output=DECODE-ROWS -v查看目标 binlog 文件,搜索DELETE FROM `your_db`.`your_table`或时间戳附近事务 - 记下该事务的
start_pos和end_log_pos(不是语句级位置,是整个事务的边界) - 注意:同一个事务里可能混着多张表操作,只提取目标表的
Delete_rows_event部分才安全 - 如果误删是通过应用批量执行的(比如 ORM 的
delete()),事务可能很大,别直接全量反转,容易误恢复其他变更
用 mysqlbinlog + sed / awk 生成反向 INSERT 语句
mysqlbinlog 本身不提供“闪回”功能,它只输出人类可读的伪 SQL(带 # at 和注释)。真正可用的 INSERT 语句得自己从 Delete_rows_event 的 @1=xxx @2=yyy 字段里提取出来,并把字段顺序、类型、NULL 值处理对——这是最容易出错的一环。
示例片段(简化):
# at 12345<br>#190101 10:00:00 server id 1 end_log_pos 12400 CRC32 0xabcde Delete_rows: table id 123 flags: STMT_END_F<br>### DELETE FROM `test`.`users`<br>### WHERE<br>### @1=1001<br>### @2='alice'<br>### @3=NULL
对应要生成的反向语句是:
INSERT INTO `test`.`users` VALUES (1001, 'alice', NULL);
- 字段顺序必须严格按表定义来,不能按
@1 @2 @3出现顺序硬套——万一中间有新增列或列顺序调整过呢 -
NULL要保留字面量NULL,不能写成空字符串或丢弃 - 时间类型(如
DATETIME)在 binlog 里可能是字符串或整数,需对照原表字段类型判断是否加引号 - 别用正则盲目替换所有
DELETE→INSERT,WHERE后面的内容才是关键数据源
跳过 GTID 或 position 执行恢复,避免主从冲突
恢复语句不能直接在生产主库上跑 source,否则会再次写入 binlog,导致从库重复执行或 GTID 冲突。必须绕过复制链路,用 --skip-gtid 或指定 SET SESSION sql_log_bin = 0 关闭日志记录。
推荐做法:
- 在从库(只读)上先验证生成的 INSERT 语句是否语法正确、数据合理
- 在主库执行前,先
SET SESSION sql_log_bin = 0;,再执行 INSERT;完事后记得SET SESSION sql_log_bin = 1; - 如果启用了 GTID,用
mysqlbinlog --skip-gtids解析,否则导入时会报GTID_PURGED错误 - 别用
mysqlbinlog | mysql管道直连,一旦中间出错没法中断或检查,应先落地成 SQL 文件人工审核
最麻烦的其实是字段类型推断和 NULL 处理——binlog 不存列名,只存变量序号,一旦表结构近期改过,@1 对应的就未必是主键了。这种时候,光靠 binlog 闪回已经不可靠,得结合 information_schema.COLUMNS 快照或者 DDL 变更记录来交叉验证。










