构建与部署 · 收录于 2026-08-30 · 适用 1 工具 ✓ 实测验证

MySQL 误删数据后如何恢复(binlog 闪回)

已验证 2026-08-30MySQL 误删数据——binlog 闪回:SHOW BINLOG EVENTS 定位误删事务起点,用 --stop-position=DELETE 起点导出并重放写入。

快速修复

# 1. 定位误删事务的起点(记下 DELETE 那行的 Pos)
SHOW BINLOG EVENTS IN "mysql-bin.000001";

# 2. 导出误删前所有写入(stop-position = DELETE 起点)
mariadb-binlog --start-position=<INSERT起点> --stop-position=<DELETE起点> /var/log/mysql/mysql-bin.000001 > flashback.sql

# 3. 恢复到新库(rewrite-db 换库名,先验证再上生产)
mariadb-binlog --start-position=<INSERT起点> --stop-position=<DELETE起点> --rewrite-db="坏库->恢复库" /var/log/mysql/mysql-bin.000001 | mysql

你会遇到什么

手滑执行了 DELETE FROM <表> 没带 WHERE(或 WHERE 条件写错),数据瞬间全没了;表里可能还插着生产数据,不能直接回滚,也不知道从哪恢复。误删后最忌讳的是继续写入或重启数据库——binlog 里可能还有救,乱操作会让数据彻底丢失。

本文路径:快速修复(30 秒一段命令)→ 一步步排查(可跳读)→ 原理与变体(症状/原因/证据)。赶时间直接跳到「快速修复」。

为什么会这样

MySQL/MariaDB 开启了 binlog(log_bin=ON,binlog_format=ROW)时,每次 DML 都会按事务记录到 binlog 文件。误删的 DELETE 也是一个普通事务,它之前的所有写入(INSERT/UPDATE)都还在 binlog 里。只要在误删事务的起始 position 处截断重放,就能把数据恢复到误删前一刻。

binlog 是 MySQL 的顺序追加日志:所有 DML 按提交顺序写成事件流,每个事务在文件里有确定的起始 position。误删的 DELETE 只是这个事件流里的一个普通事务——它之前的所有写入(INSERT/UPDATE)都还在。flashback 的本质是「把 binlog 当成可回放的时间线」:定位误删事务的起点,重放它之前的事件,数据就回到误删前一刻。

可行性前提:binlog_format=ROW(row 格式才含完整前后镜像,statement 格式无法精确重建);log_bin=ON(没开就没有时间线)。

确定性推论:能不能闪回先查两个开关(SHOW VARIABLES LIKE 'binlog_format' / log_bin),ROW + ON 才可行;恢复要在独立实例上重放(binlog 重放会执行写操作,别在生产库直接跑);定位 position 靠 mysqlbinlog 按时间/关键字找误删语句;

判断路径:报错「无 binlog」= 环境没开,闪回不可用,只能走备份。

一步步来

1. 紧急止损(第一步最重要) — 必做

FLUSH TABLES WITH READ LOCK;   # 锁库禁止写入
cp -r /var/log/mysql/ /backup/ # 备份 binlog(防止被清理)

绝对不要重启 MySQL——内存里未落盘的变更可能丢失;也不要继续写数据,新的写入会污染恢复点。

2. 确认 binlog 开启并列出日志 — 必做

SHOW VARIABLES LIKE "log_bin";  # 必须 ON,否则只能靠备份
SHOW BINARY LOGS;               # 找到最新的 binlog 文件

3. 用 SHOW BINLOG EVENTS 定位误删事务 — 排查

SHOW BINLOG EVENTS IN "mysql-bin.000001";

关键看两行:INSERT 事务的 Annotate_rows(数据写入)和 DELETE 事务的 Annotate_rows(误删)。DELETE 事务的 Pos 列就是恢复截止点。

4. 闪回导出 + 重放(先恢复到新库验证) — 必做

# 导出误删前区间(含 INSERT 不含 DELETE)
mariadb-binlog --start-position=<INSERT事务Pos> --stop-position=<DELETE事务Pos> /var/log/mysql/mysql-bin.000001 > flashback.sql

# 建恢复库 + 重放(rewrite-db 避免覆盖坏库)
mariadb-binlog --start-position=<INSERT事务Pos> --stop-position=<DELETE事务Pos> --rewrite-db="原库->恢复库" /var/log/mysql/mysql-bin.000001 | mysql

# 验证行数与内容一致后,再决定切回生产
SELECT COUNT(*) FROM 恢复库.表;

怎么确认修好了

SELECT * FROM 恢复库.表;

行数与误删前一致(本机实测:5 行全删后恢复 5 行,内容/顺序完全一致),且闪回 SQL 里 grep DELETE 为 0(没有把误删动作重放进去)。

其它环境的表现

环境说明
MySQL 8.x命令为 mysqlbinlog,机制相同;binlog 默认开启
MariaDB 11.8命令为 mariadb-binlog(mysqlbinlog 为兼容别名);本机实测环境
DROP TABLE/库需要全量备份 + 备份点之后的 binlog 重放,闪回只覆盖 DML
binlog 被清理只能靠物理备份,覆盖部分丢失
未实测项DROP 场景全量恢复、binlog2sql 反向 SQL、超长 binlog 性能

症状

手滑 DELETE 不带 WHERE 全表被删(或 UPDATE 误改全表);数据在 binlog 开启的库中仍有痕迹;重启数据库或继续写入会丢失恢复机会。

原因

binlog(ROW 格式)按事务记录全部 DML;误删 DELETE 是独立事务,其起始 position 之前的写入仍完整保存在 binlog。用 mariadb-binlog/mysqlbinlog --start-position --stop-position 精确导出误删前区间并重放,即可恢复到误删前一刻;--rewrite-db 可先恢复到新库验证。

解法

  1. 止损:FLUSH TABLES WITH READ LOCK + 备份 binlog,不重启、不写入;
  2. SHOW BINARY LOGS + SHOW BINLOG EVENTS 定位误删事务起点 position;
  3. mariadb-binlog --start-position=INSERT起点 --stop-position=DELETE起点 导出闪回 SQL(只含误删前写入);
  4. --rewrite-db 重放到恢复库验证,确认后切回生产。
验证证据
  • 本地实测(2026-08-30,Ubuntu WSL + MariaDB 11.8.6):开启 binlog(ROW)→ 建表插 5 行 → DELETE FROM orders 全删 → SHOW BINLOG EVENTS 定位(INSERT 事务 869~1335,DELETE 事务 1335 起)→ --start-position=869 --stop-position=1335 闪回导出(grep DELETE = 0)→ --rewrite-db 重放到恢复库 → 5 行 100% 恢复,内容顺序一致。
  • 实测复现日志(2026-08-30)

适用工具 (1)

本问题方案已挂到下列工具的方案页——从工具页也能回到这里,双向可达。

mysql · 外部工具

来源与证据

收录 验证 ✓ 实测验证适用 1 工具
方案含本地实测或来源可复核的解法;未实测项已在文中如实标注。

素材来源:https://www.cnblogs.com/zrui-xyu/p/22563082

← 构建与部署 问题域 | 问题货架 | 遇到其它问题?去 问题空间 提交求助卡