能恢复,但必须满足binlog已开启、格式为row、误删操作在最近可用binlog内三个硬性条件;需验证binlog_format=row、log_bin=on、binlog_row_image=full;用位置点精准定位事务范围;推荐my2sql生成带完整where的回滚sql;执行前需禁用外键、处理主键冲突、统一字符集并指定目标库。

确认 binlog_format 是 ROW 且 log_bin 已启用
恢复单表数据的前提不是“有 binlog”,而是它记录了该表的**完整行变更**。STATEMENT 或 MIXED 格式下,DELETE FROM t 只记语句本身,无法还原被删的行内容;只有 binlog_format=ROW 才能提取出每条被删记录的字段值。
执行以下命令验证:
-
SHOW VARIABLES LIKE 'binlog_format';—— 必须返回ROW -
SHOW VARIABLES LIKE 'log_bin';—— 必须返回ON -
SHOW VARIABLES LIKE 'binlog_row_image';—— 推荐为FULL(否则可能缺失部分字段) -
SHOW BINARY LOGS;—— 确认目标时间段内的日志文件仍存在,未被PURGE或过期清理
用 mysqlbinlog 定位目标表的 DELETE_ROWS_EVENT
不能靠关键词搜索表名,因为 binlog 是二进制格式;必须用 mysqlbinlog 解析并人工筛选事件。重点找 DELETE_ROWS_EVENT,它会明确标注库名、表名和被删行的完整字段值(形如 ### @1=123 ### @2='abc')。
推荐命令(带时间范围 + 行解码):
mysqlbinlog --base64-output=DECODE-ROWS -v \ --start-datetime="2026-08-12 10:00:00" \ --stop-datetime="2026-08-12 11:30:00" \ /var/lib/mysql/mysql-bin.000023
注意:
- 服务器时区可能与你本地不一致,先查
SELECT NOW();对齐时间 - 如果启用了 GTID,可用
--include-gtids精确过滤事务,但需确保gtid_mode=ON - 输出中跳过
Write_rows_event和Update_rows_event,只盯住DELETE_ROWS_EVENT及其下方的###行 - 记下该事件的
end_log_pos值,这是后续生成回滚 SQL 的截止位置
用 my2sql 生成带 WHERE 条件的 INSERT 回滚语句
手动拼 INSERT 很容易漏字段或类型错配,尤其遇到 TIMESTAMP、JSON 或 NULL 值。my2sql 能自动解析 ROW 格式日志,还原出可直接执行的 INSERT INTO t (...) VALUES (...),且 WHERE 条件精确到主键或唯一键。
基本用法:
my2sql -user root -password 123456 \ -host 127.0.0.1 -port 3306 \ -work-type flashback \ -B -d test -t person \ -start-file mysql-bin.000023 \ -start-pos 123456 -stop-pos 789012
关键参数说明:
-
-work-type flashback:生成逆向语句(即把 DELETE 转成 INSERT) -
-B:跳过 BEGIN/COMMIT 包裹,方便单独执行 -
-d test -t person:限定只处理test.person表,避免混入其他表操作 -
-start-pos / -stop-pos:用上一步记下的位置点,精准圈定事务范围
输出结果是纯 SQL 文件,内容类似:
INSERT INTO `test`.`person` (`id`, `name`) VALUES (123, 'Alice');
执行前必须禁用外键并处理主键冲突
直接执行生成的 INSERT 很可能失败——不是语法错,而是约束或冲突问题。
务必在目标库执行以下准备动作:
- 关闭外键检查:
SET FOREIGN_KEY_CHECKS = 0;(否则关联表缺失时会报错) - 若表有自增主键,且原记录已存在,INSERT 会触发
Duplicate entry错误;此时需先DELETE冲突行,或改用INSERT IGNORE/REPLACE INTO(注意后者会覆盖整行) - 确认字符集一致:
SHOW CREATE TABLE test.person;查源表定义,目标库建表语句需完全匹配,尤其CHARSET和COLLATE - 指定目标库执行:
mysql -D test -u root -p ,避免跨库写错
真正麻烦的从来不是找到日志,而是搞清那几行 ### @1=... 里哪个是主键、哪个允许为 NULL、时间字段要不要加引号——这些细节不核对,SQL 就跑不通。











