# 一次 MySQL DELETE 误操作的数据恢复尝试实录

## 背景

2026年1月20日中午,有人在生产环境执行了一条 DELETE 清理脚本,想删除 xxx 表中属于2026年订单的数据。

结果:删了 206,689 行。


## 第一步:还原操作过程

查生产sql日志

11:51:12

CREATE TABLE xxx_2026 ... 11:51:27

INSERT INTO xxx_2026 SELECT * FROM ... (备份完成) 11:53:00

DELETE FROM xxx WHERE orderno IN (SELECT orderno FROM tb_order_2026) (执行耗时: 116.287s)

**操作逻辑**:tb_order.sh 脚本的标准流程是------先 INSERT 到年度备份表,再 DELETE 主表。备份已完成,DELETE 也执行了。
**删了多少?**
`_2026` 备份表记录了 206,689 行,这基本上就是被 DELETE 掉的行数。但精确数字因为日志被截断,始终没能确认。

## 第二步:发现第二个 DELETE 没执行

脚本本意是删两批:

  • 第一批:2026-01-01 ~ 2026-01-06 的订单 ✅ 已执行

  • 第二批:2026-01-06 ~ 2026-01-11 的订单 ❌ 未执行

查日志发现,第二次 DELETE 报了语法错误:

SQL 语法错误: DELETE FROM ... WHERE "orderno" IN (...)

**原因**:Navicat 传参时 SQL 被双引号包住,导致语法错误,第二次 DELETE 根本没有执行。这意味着 2026-01-06~11 的数据还在主表里,没丢。

## 第三步:停止定时任务,防止再次触发

crontab 显示,每月 15/16/23 号凌晨 3 点会执行 tb_order.sh:

每月15/16/23号凌晨跑清理脚本

0 3 15,16,23 * * /opt/tb_order.sh 0 4 15,16,23 * * /opt/tb_pay_info.sh

紧急执行:

```bash

systemctl stop crond

暂停定时任务,防止23号凌晨脚本再次执行。

第四步:评估恢复方案

方案一:binlog 闪回

mysqlbinlog --start-datetime="2026-01-20 11:50:00" \

--stop-datetime="2026-01-20 12:00:00" \

/var/lib/mysql/mysql-bin.000001 | grep "DELETE"

结果:无 binlog。该 数据库为自建的 实例没有开启 binlog,无法用 mysqlbinlog 恢复。

方案二:HBR 全量备份

# 查 HBR 备份日志

grep "2026-01-19" /usr/local/aegis/hbrclient/logs/backup_job-*.log

结果:

backup_job-14940.log: 2026-01-19 02:02:00 开始全盘备份

backup_job-14940.log: 2026-01-19 03:00:06 全盘备份完成

HBR 每天凌晨 2 点有全盘备份,包含 /var/lib/mysql。理论上可以从 HBR 恢复一个副本,再导出被删数据后插回主表。

但 HBR 恢复操作复杂,需要:申请副本 → 挂载 → 启动临时实例 → 导出数据 → 清理副本。步骤多,风险高,暂时没有执行。

方案三:INSERT IGNORE 回滚(最终方案)

_2026 备份表有 206,689 行,结构已知(id 主键 + orderno 索引)。

回滚 SQL:

sql

-- 分批插回,每批1000行

INSERT IGNORE INTO xxx

SELECT * FROM xxx_2026

WHERE id BETWEEN 1 AND 1000;

INSERT IGNORE INTO xxx

SELECT * FROM xxx_2026

WHERE id BETWEEN 1001 AND 2000;

-- ... 以此类推

INSERT IGNORE 的好处:如果 2026-01-06~11 的数据(没被删的那批)和 _2026 里有主键冲突,不会报错直接跳过。

但回滚最终没有执行完------因为 SHOW CREATE TABLE 结果没拿到,无法精确确认主键列名,回滚 SQL 暂停。

踩的坑
坑一:SQL 被双引号包住导致语法错误

Navicat 传参习惯会把变量用双引号包住,但 MySQL 里字符串应该用单引号,双引号是用于表名列名的。这导致第二次 DELETE 失败,数据没有按预期删除干净。

教训:Navicat 传参时确认 SQL 语法,不要依赖 GUI 自动加引号。

坑二:日志里的 COUNT 结果被截断

11:51:27 INSERT INTO xxx_2026 SELECT * FROM ...

11:51:27 SELECT COUNT(*) FROM ...

查询执行了 11.733s,日志里能看到语句发出,但结果数字被截断。始终不知道精确删了多少行,只能靠 _2026 表的行数反推。

教训:生产执行重要 SQL 前,先在测试库跑一遍确认结果。

坑三:没有 binlog

该 RDS 实例未开启 binlog,导致无法用 mysqlbinlog 做增量恢复。

教训:生产数据库务必开启 binlog,是数据安全的基本保障

HBR 是全盘备份,包含 /var/lib/mysql,但不包含 binlog 日志文件(binlog 是独立的)。即使恢复了 HBR 副本,也只能恢复到快照点的全量数据,无法做增量。

教训:HBR + binlog 双重保障才是完整的生产数据库备份策略。

总结

DELETE 删了多少

约 206,689 行(从备份表反推)

2026-01-01~06 数据

已备份到 _2025 表,未回滚

2026-01-06~11 数据

因语法错误未删除,仍在主表

定时任务

已停止 crond

binlog

未开启,无法 mysqlbinlog 闪回

HBR 备份

2TB,包含 MySQL 全量数据

INSERT IGNORE 回滚

因主键列名未确认,未执行完

**经验总结

  1. 生产执行 DELETE 前必做检查清单**

□ 在测试库跑一遍,确认影响行数

□ 确认备份已就位(INSERT SELECT 完成)

□ 确认 crontab 已暂停或该时段无定时任务

□ 确认 binlog 已开启

□ 确认 WHERE 条件无误(特别检查引号是单引号)

□ 预估执行时间,设置合适的 max_execution_time

2. 备份表命名规范

备份表加时间戳后缀:xxx_2025 → xxx_20250623_1151,这样即使多次操作,备份不会覆盖。

-- 检查是否开启

SHOW VARIABLES LIKE 'log_bin';

SHOW VARIABLES LIKE 'expire_logs_days';

  1. 脚本 DELETE 改为 UPDATE 标志位

tb_order.sh 逻辑是 INSERT + DELETE,更安全的做法是:

-- 不删除,只标记删除状态

UPDATE xxx

SET deleted = 1, delete_time = NOW()

WHERE orderno IN (SELECT orderno FROM tb_order_2025);

这样数据不丢失,随时可以回滚。

相关命令速查

# 查 binlog 是否开启

mysql -e "SHOW VARIABLES LIKE 'log_bin';"

# 查 HBR 备份日志

grep "backup_job" /usr/local/aegis/hbrclient/logs/backup_job-*.log | tail -10

# 查 crontab

crontab -l

cat /etc/cron.d/*

# 暂停定时任务

systemctl stop crond

# 查备份表行数

SELECT COUNT(*) FROM xxx_2025;

# 分批 INSERT IGNORE 回滚(示例)

INSERT IGNORE INTO xxx

SELECT * FROM xxx_2025

WHERE id BETWEEN 1 AND 1000;

相关推荐
Dxy12393102162 小时前
MySQL索引完整教程:创建、查看、修改、删除与日常管理
前端·javascript·mysql
Quincy_Freak4 小时前
信创内网数据规范实践:银河麒麟下SQLite本地数据安全管理方案
数据库·sqlite·arm·数据库管理·大数据分析·银河麒麟·aarch64
至乐活着4 小时前
MySQL索引底层原理与查询优化实战:从B+树到执行计划
mysql·b+树·查询优化·sql性能·索引原理
2601_962683896 小时前
治理遗留系统中的“生肉 SQL”:一次用多模型协作优化慢查询的实战复盘
数据库·人工智能·sql
酱学编程6 小时前
【从零到一实现一个 AI Agent 框架 · 第四篇】04. 任务规划:拆解复杂目标 -
服务器·网络·数据库·人工智能
shushangyun_7 小时前
2026智能采购商城系统选型指南:如何引领企业数字化采购升级
java·大数据·数据库·人工智能·机器学习
华山令狐虫8 小时前
DBAPI AI 写 SQL:支持动态 SQL 与参数占位符,自然语言一键生成
数据库·人工智能·sql·dbapi
IvorySQL9 小时前
PG 技术日报|2026-07-04
数据库·人工智能·postgresql·开源
Hoxy.R9 小时前
KingbaseES读写分离高可用集群扩容、备库重建与故障切换实战
运维·数据库