必须确认binlog已开启且格式为row:show variables like 'log_bin'返回on,且show variables like 'binlog_format'返回row;否则无法精准还原误删行。

确认 binlog 是否开启且格式为 ROW
MySQL 默认不开启 binlog,或开启但格式为 STATEMENT,这两种情况都导致无法精准还原被删的行。必须先验证:SHOW VARIABLES LIKE 'log_bin'; 返回 ON,且 SHOW VARIABLES LIKE 'binlog_format'; 返回 ROW。
常见错误现象:执行 mysqlbinlog 解析后看不到具体 DELETE 的行数据,只看到类似 DELETE FROM t1 WHERE id=1 —— 这是 STATEMENT 格式的表现,无法还原条件不明确的误删(比如没带 WHERE 的全表删)。
- 如果
log_bin是OFF,立刻停写,备份当前数据目录,后续只能依赖物理备份或从库 - 如果格式是
MIXED或STATEMENT,需临时改配置、重启,并确认业务能接受该变更(部分函数/存储过程在ROW下不可复制) -
ROW格式下,mysqlbinlog输出里会出现### DELETE FROM `db`.`t1`+### WHERE+### @1=1这类字段级标记,这才是可逆操作的基础
定位误删操作对应的时间点或 position
不能靠“大概几点删的”去盲试,必须用日志定位精确边界。最稳妥的是用时间范围 + 表名过滤,再人工核对事件上下文。
使用场景:已知误删发生在 2024-06-15 14:22:30 左右,目标表是 orders,且删除前无其他并发 DML 干扰。
- 先查出对应时间段的 binlog 文件:
SHOW BINLOG EVENTS IN 'mysql-bin.000012' FROM 12345 LIMIT 20;,或用mysqlbinlog --base64-output=DECODE-ROWS -v --start-datetime="2024-06-15 14:22:00" --stop-datetime="2024-06-15 14:23:00" /var/lib/mysql/mysql-bin.000012 | grep -A 5 -B 5 "DELETE.*orders" - 重点看每个
DELETE事件前的Xid =值,它标识事务边界;一个事务内多条语句要一起回滚,不能只挑DELETE行 - 避免直接用
--stop-position截断——容易切在事务中间,导致恢复后主键冲突或数据不一致
生成反向 SQL(INSERT 替代 DELETE)并安全应用
binlog 本身不存“反向操作”,得靠解析出被删的行值,手动拼成 INSERT。工具如 mysqlbinlog --base64-output=DECODE-ROWS -v 可读,但不能直接执行;必须二次处理。
性能与兼容性影响:大表全删后恢复,若一次性插入几百万行,会阻塞复制、撑爆 buffer pool,甚至触发 max_allowed_packet 错误。
- 不要用 sed/awk 直接替换
DELETE→INSERT:WHERE子句里的@1=123需转成实际字段名和值,且 NULL、BLOB、JSON 字段需特殊处理 - 推荐用 Python 脚本解析输出(或现成工具如
binlog2sql),它能把### @1=1 ### @2=NULL映射为INSERT INTO orders(id, name) VALUES(1, NULL) - 恢复前务必在测试库验证 SQL 是否语法合法、字段顺序匹配;线上执行时加
SET sql_log_bin=0;避免再次写入 binlog 形成循环
跳过已恢复事务防止重复写入
如果恢复过程中主库还在写,或者你用了多个 binlog 文件拼接恢复,极可能把同一事务重放两次——轻则主键冲突报错,重则覆盖正确数据。
容易踩的坑:以为“从某个 position 开始恢复就安全了”,却忽略了 GTID 模式下 position 不连续、或 relay log 里已有部分事件已被应用。
- 用 GTID 时,恢复前执行
SELECT @@global.gtid_executed;,确认待恢复事务的 GTID 不在其中;恢复后用SELECT * FROM mysql.gtid_executed;核对 - 非 GTID 模式,记录恢复起始的
mysql-bin.000012:123456,并在恢复命令中显式指定--start-position=123456,且确保后续CHANGE MASTER TO ... MASTER_LOG_FILE指向下一个文件 - 恢复完立即检查表行数、校验关键字段(如
MAX(id)、COUNT(*)),别等第二天业务报警才发觉漏了一半
真正麻烦的不是找日志,是判断哪几行该恢复、哪几行其实是业务逻辑本该删的——binlog 不记录“为什么删”,只记录“删了什么”。所以恢复前,最好和业务方对齐那条 DELETE 是不是真的误操作。











