MySQL恢复误删数据的前提是Binlog已启用且格式为ROW;需先确认log_bin=ON、binlog_format=ROW、binlog_row_image=FULL;再定位误删事件的binlog文件与位置;接着用binlog2sql等工具将Delete_rows事件反向生成INSERT语句;最后跳过误删事务,重放后续binlog至最新状态。

确认 Binlog 是否启用且格式正确
MySQL 默认不开启 Binlog,恢复前提是你提前开启了它,并且 binlog_format 不是 STATEMENT(尤其对非确定性函数、now()、sysdate() 等会导致恢复错乱)。生产环境强烈建议设为 ROW。
检查方式:
SHOW VARIABLES LIKE 'log_bin';<br>SHOW VARIABLES LIKE 'binlog_format';<br>SHOW MASTER STATUS;
如果 log_bin 值为 OFF,那根本没法恢复——Binlog 从没记录过任何变更。
-
log_bin必须为ON -
binlog_format推荐ROW;MIXED在部分场景下也行,但不如ROW可靠 - 还要确认
binlog_row_image是FULL(默认值),否则可能缺失旧值,无法反向生成INSERT
定位误删操作所在的 binlog 文件和位置
删库删表这类 DDL 或大范围 DELETE 会直接写入 Binlog(ROW 格式下每行变更都记),但你需要先找到它发生的时间点或事务起始偏移。
常用方法:
- 用
mysqlbinlog解析最近的 binlog 文件,按时间或事件类型过滤:mysqlbinlog --base64-output=DECODE-ROWS -v mysql-bin.000003 | grep -A 5 -B 5 "DELETE FROM `user`"
- 结合
SHOW BINLOG EVENTS IN 'mysql-bin.000003' FROM 12345逐步翻查,重点看Query事件(DDL)或Write_rows/Delete_rows事件(DML) - 如果知道误操作大概时间,用
--start-datetime和--stop-datetime缩小范围,避免解析整个文件
注意:mysqlbinlog 输出里,每个事务以 BEGIN 开头、COMMIT 结尾;DELETE 操作在 ROW 格式中表现为 Delete_rows 事件,里面含被删行的完整字段值(这就是能反向还原的关键)。
提取并反转 Delete_rows 事件生成 INSERT 语句
Binlog 本身不存“反向 SQL”,得靠工具或手动把 Delete_rows 事件转成 INSERT。官方 mysqlbinlog 加 -v 参数可输出伪 SQL,但它是只读展示,不能直接执行。
实操建议:
- 用
mysqlbinlog --base64-output=DECODE-ROWS -v输出后,人工提取### DELETE FROM ...下面的### SET行(即被删数据),改写成INSERT INTO ... VALUES (...) - 更稳妥的方式是用开源工具如
binlog2sql(Python 写):python binlog2sql.py -h127.0.0.1 -P3306 -uadmin -p'pwd' -dtest -tstudent --start-file='mysql-bin.000003' --start-pos=12345 --stop-pos=23456 -B
,加-B参数会直接输出回滚用的INSERT语句 - 切勿直接用
mysqlbinlog的原始输出去重放——它含SET TIMESTAMP、SET @@session.*等上下文,可能引发主从不一致或权限错误
关键点:binlog2sql 依赖 information_schema 获取表结构,如果误删的是整个库,需先从备份或其它环境还原表结构,否则解析失败。
跳过误删事件,重放后续 binlog 到最新状态
光恢复被删数据不够,你还得把误删之后、到当前时间之间的所有正常变更补上,否则数据就“倒退”了。
典型流程:
- 先停应用或锁表,防止新写入干扰
- 用
mysqlbinlog把误删位置前的日志导出并重放(恢复误删前状态) - 再从误删事件的
end_log_pos开始,截取后续 binlog,过滤掉误删那条(或整个事务),把其余内容重放 - 推荐用
--exclude-gtids(MySQL 5.6+ GTID 模式)或--start-position+--stop-position精确控制范围
最容易被忽略的是 GTID 模式下的冲突处理:如果误删事务已提交且被从库同步过,强行跳过会导致主从 GTID 集不一致,必须配合 SET GTID_NEXT 注入空事务来占位,否则 START SLAVE 会报错。


















