必须确保 MySQL 开启 binlog 且格式为 ROW,否则 binlog2sql 无法还原单条记录;需精确定位误删时间点和 binlog 位置,再用 binlog2sql 加 --where 和 --flashback 生成带完整字段的逆向 INSERT,并在测试库验证后执行。

确认 MySQL 是否开启了 binlog 且格式为 ROW
没开 binlog 或设成 STATEMENT 格式,binlog2sql 就没法还原单条记录——它依赖每行数据的完整变更快照。执行以下命令检查:
mysql -e "SHOW VARIABLES LIKE 'log_bin'; SHOW VARIABLES LIKE 'binlog_format';"
必须看到 log_bin = ON 且 binlog_format = ROW。如果 binlog_format 是 MIXED 或 STATEMENT,即使日志存在,binlog2sql 也无法提取出被删行的具体字段值,生成的回滚 SQL 会是空或错误的 DELETE。
定位误删操作发生的时间点和 binlog 文件
靠猜时间容易漏掉或跨过目标事件,建议先用 mysqlbinlog 快速扫描。例如误删发生在 2024-06-15 14:23 左右,先查当前 binlog 列表:
mysql -e "SHOW MASTER LOGS;"
然后逐个检查最近的 binlog(如 mysql-bin.000012)中是否有 DELETE 操作:
mysqlbinlog --base64-output=DECODE-ROWS -v mysql-bin.000012 | grep -A 5 -B 5 "DELETE.*FROM \`mydb\`.\`users\`"
找到匹配后,记下精确的 timestamp 和 end_log_pos(比如 2024-06-15T14:23:08、pos=123456)。这些是 binlog2sql 的关键过滤条件,少一个都可能捞出无关语句。
用 binlog2sql 生成带 WHERE 条件的逆向 INSERT
binlog2sql 默认输出的是“反向操作”,对 DELETE 就是 INSERT,但它不会自动加 WHERE 来限定单条记录——你得靠主键或唯一键值缩小范围。假设误删的是 users 表中 id = 1001 的记录:
- 先确保已安装并配置好
binlog2sql(需 Python 3.6+,且 MySQL 账户有SELECT,REPLICATION SLAVE,REPLICATION CLIENT权限) - 运行命令时必须指定
--start-datetime和--stop-datetime(或--start-position/--stop-position),否则可能扫完整个 binlog,耗时且结果混杂 - 用
--flashback参数触发逆向生成,再用-d mydb -t users锁定库表,最后靠--where="id=1001"精确命中(注意:这个--where是在解析阶段过滤 event,不是 SQL 中的 WHERE)
完整命令示例:
python3 binlog2sql.py -h 127.0.0.1 -P 3306 -u root -p'xxx' -d mydb -t users \ --start-datetime "2024-06-15 14:22:00" --stop-datetime "2024-06-15 14:24:00" \ --flashback --where="id=1001"
输出会是一条 INSERT INTO users (...) VALUES (...),字段顺序和值都来自 binlog 中该 DELETE 事件前的镜像(即被删前的数据)。
执行前务必验证逆向 SQL 的字段完整性
binlog2sql 生成的 INSERT 不一定包含所有字段:如果表有 DEFAULT、GENERATED 或 NULL 列,而 binlog 中未显式记录其值,生成的 SQL 可能漏掉这些列,直接执行会报错或写入异常值。
检查方法很简单:把生成的 SQL 复制出来,对照表结构 DESCRIBE mydb.users; 一行行核对字段名和是否允许 NULL。特别注意:
- 自增主键(
id)通常会被显式写出,但如果原 DELETE 前该行是通过INSERT ... SELECT或批量导入产生,可能不包含id字段,此时需手动补上或改用INSERT IGNORE - 时间戳字段(如
created_at)若设了CURRENT_TIMESTAMP,binlog 里可能只存了实际值,但恢复时应保留原值而非用 NOW() - 如果表启用了外键约束,且关联表数据已被删,这条
INSERT会失败,得先处理依赖关系
最稳妥的做法是:先在测试库跑一遍生成的 SQL,看是否语法通过、字段数匹配、无外键冲突——线上直接执行没有后悔键。


















