能恢复,但必须满足三个硬性条件:binlog已开启、格式为ROW、误删操作发生在当前保留的binlog文件内;缺一不可,否则直接放弃该路径。确认log_bin=ON、binlog_format=ROW、binlog_row_image=FULL后,用mysqlbinlog按时间或位置定位DELETE_ROWS_EVENT,再通过binlog2sql生成精准INSERT语句,禁用外键并校验字符集后执行,最后跳过误删事务重放后续binlog以保持数据一致性。

能恢复,但必须满足三个硬性条件:binlog已开启、格式为ROW、误删操作发生在当前保留的binlog文件内;缺一不可,否则直接放弃该路径。
确认 binlog_format=ROW 且 log_bin=ON
恢复不是“有日志就能搞”,而是日志里得存了被删行的完整字段值。只有ROW格式才保证这点;STATEMENT只记DELETE FROM t WHERE id=1这种语句,根本不知道删的是哪几行;MIXED可能降级成STATEMENT,同样不可靠。
登录 MySQL 后执行:
-
SHOW VARIABLES LIKE 'log_bin';—— 必须返回ON -
SHOW VARIABLES LIKE 'binlog_format';—— 必须是ROW -
SHOW VARIABLES LIKE 'binlog_row_image';—— 推荐FULL(默认值),避免字段缺失
如果任一不满足,恢复失败。别尝试“先改配置再重启”——误删已发生,改完也追不回过去的数据。
用 mysqlbinlog 定位 DELETE_ROWS_EVENT 的位置
ROW 格式下,删除行为体现为DELETE_ROWS_EVENT,它包含被删行所有字段的原始值(如### @1=123、### @2='alice'),这才是能反向生成INSERT的关键。
不要直接解析全部 binlog 文件,太慢且易漏。优先按时间范围缩小:
- 先查当前 binlog 列表:
SHOW BINARY LOGS;,确认文件还存在(别找已被PURGE掉的) - 用
mysqlbinlog --base64-output=DECODE-ROWS -v --start-datetime="2026-08-12 09:45:00" --stop-datetime="2026-08-12 09:50:00" /var/lib/mysql/mysql-bin.000042 - 输出中搜
DELETE_ROWS_EVENT,找到对应事务的end_log_pos(即下一个事件起始位置),记下来
注意:服务器时区可能和你本地不一致,用SELECT NOW();对齐后再估算时间范围。
用 binlog2sql 生成可执行的 INSERT 回滚语句
手动拼INSERT极易出错:字段顺序错、类型不匹配、NULL 处理漏、字符集乱码。必须用工具解析二进制事件,而不是读mysqlbinlog的伪 SQL 输出。
binlog2sql(Python 工具)能直接把DELETE_ROWS_EVENT转成带完整字段和值的INSERT语句:
- 命令示例:
python binlog2sql.py -h127.0.0.1 -P3306 -uuser -p'pwd' -dmydb -tusers --start-file='mysql-bin.000042' --start-pos=123456 --stop-pos=123999 - 输出是纯
INSERT INTO users (...) VALUES (...);,可直接导入 - 执行前务必:
SET FOREIGN_KEY_CHECKS=0;(禁用外键)、指定目标库(避免插到错库)、确认字符集一致(尤其含中文时)
别跳过这些检查——主键冲突或外键报错会中断回滚,而字符集不一致可能导致数据变乱码,表面成功实则损坏。
跳过误删事务后重放后续 binlog
只补回被删数据还不够。如果你在mysql-bin.000042里恢复了误删,但之后还有mysql-bin.000043等新日志,它们记录了误删后的新写入。不重放这些,数据就停留在误删前的状态,业务逻辑会断层。
标准做法是:
- 用
mysqlbinlog提取从误删事务end_log_pos开始、到最新MASTER STATUS位置的所有日志 - 过滤掉误删事务本身(可用
--exclude-gtids或人工删掉对应区间) - 将剩余日志重放进数据库:
mysql -uuser -p
最容易被忽略的一点:整个过程必须在应用停写或数据库设为read_only状态下进行。边恢复边写入,会导致新旧数据混杂,结果不可预测。


















