前事不忘,后事之师,不忘国耻!

 用户注册  找回密码
 用户注册
搜索
查看: 12|回复: 0

[开发应用] MySQL 误删数据恢复实战:从 binlog 定位到闪回重建

[复制链接]

[开发应用] MySQL 误删数据恢复实战:从 binlog 定位到闪回重建

[复制链接]
dbaai

主题

0

回帖

286

积分

DBAAI

积分
286
前天 07:49 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?用户注册

×
MySQL 误删数据恢复实战:从 binlog 定位到闪回重建


一、具体的问题

周五下午四点多,运营同事反馈订单列表少了大半天的数据。排查发现,一个新上线的数据订正脚本条件写错,把 orders 表里 create_time > '2026-10-09' 误写成了 create_time > '2026-09-09',一条不带 LIMIT 的 UPDATE 把近一个月的状态字段全刷成了同一个值,影响约 18 万行。更麻烦的是,业务在这之后还在持续写入——也就是说,脏数据和好数据已经混在一起了。

当时的恢复诉求很明确:不能整库回滚到昨天(会丢掉今天全部正常业务),只把这张表这批被误改的行恢复回去,而且停机时间越短越好。最终我们靠「全量备份 + binlog 重放 + 闪回误操作事务」的组合在 40 分钟内完成,一条数据没丢。这套流程拆开讲。

二、核心原理

误删/误改数据的恢复,底层只有一条路:全量备份恢复出一个"干净的过去",再用 binlog 把这段过去之后的正常变更重放回来,最后把误操作那一个事务逆向抵消掉。

binlog 闪回的前提常被忽视:binlog_format=ROW 且 binlog_row_image=FULL 时,binlog 里记录了每行变更的前镜像(改之前的值)和后镜像。闪回工具(my2sql、binlog2sql 等)就是拿前镜像生成逆向 SQL。如果还是 STATEMENT 格式,binlog 里只有 UPDATE ... WHERE ... 这条语句本身,没有每行的旧值,闪回就无从谈起——很多老库默认还是 STATEMENT,出事那天才发现就晚了。

另一个关键区分:DELETE/UPDATE 这类 DML 可以闪回,但 DROP TABLE、TRUNCATE 是 DDL,binlog 里只记一条语句,没有任何行数据。DDL 误操作只能走全量恢复 + 重放到 DDL 执行前,这条路必须提前有备份撑着,事后是造不出来的。

还有一条铁律:一切重放和闪回都在临时实例上做完、校验通过之后,再导回生产。 直接在主库上重放 binlog 或跑回滚 SQL,一旦中断或重放错位,主从复制立刻错乱,事故升级成双倍事故。

三、实例参考(动手步骤)

步骤 0:止血与基线确认。 先停应用写入或对库设只读,防止脏数据继续扩散:
  1. SET GLOBAL super_read_only = ON;
  2. SHOW VARIABLES WHERE Variable_name IN
  3. ('log_bin','binlog_format','binlog_row_image','binlog_expire_logs_seconds');
  4. SELECT COUNT(*) FROM orders WHERE update_time > '2026-10-09 15:00';
复制代码

三项变量只要有一项不满足(log_bin=OFF / STATEMENT / MINIMAL),闪回路就走不通,直接转全量恢复方案,别浪费时间试。同时和业务方对账受影响行数,作为恢复后的验收基准。

步骤 1:定位误操作事务。 用 mysqlbinlog 解码,找到误操作的 GTID 和起止 position:
  1. mysqlbinlog --no-defaults --base64-output=decode-rows -vv \
  2.   --start-datetime='2026-10-09 15:00:00' \
  3.   binlog.000123 | grep -B5 -A20 'UPDATE `orders`'
复制代码

记下误操作事务的 GTID(假设 GTID 3-1-88231,XID 起止 position),这是后面重放边界的依据。装有 my2sql 的环境可以直接 my2sql -mode repl 拉出人类可读 SQL,定位更快。

步骤 2:临时实例恢复全量备份。 取最近一次 XtraBackup 全备,恢复到同机的 3307 临时实例,绝不动生产:
  1. xtrabackup --copy-back --target-dir=/data/bak/20261009_full/ \
  2.   --datadir=/data/mysql3307/data/
  3. chown -R mysql:mysql /data/mysql3307/ && systemctl start mysql3307
复制代码

步骤 3:binlog 重放到误操作前。 把备份完成时刻到误操作事务之前的所有 binlog 重放进临时实例:
  1. mysqlbinlog --start-position=4 --stop-position=18765544 \
  2.   binlog.000122 binlog.000123 | mysql -h127.0.0.1 -P3307 -uroot -p
复制代码

--stop-position 一定要落在误操作事务开始之前。重放完核对临时实例的数据到了"误操作前一秒"。

步骤 4:闪回误操作事务。 用 my2sql 对误操作那段 binlog 生成逆向 SQL:
  1. my2sql -user repl -password 'xxx' -host 127.0.0.1 -port 3306 \
  2.   -start-file binlog.000123 -start-pos 18765544 -stop-pos 18910222 \
  3.   -mode file -local-dir ./flashback_out -flashback
复制代码

产物是按倒序生成的回滚 SQL。在临时实例上执行它,然后核对:被误改的 18 万行全部回到旧值,且 15:00 之后的正常业务数据原样保留。

步骤 5:单表校验后导回生产。 只导目标表,不做整库同步:
  1. mysqldump -h127.0.0.1 -P3307 orders --no-create-info \
  2.   --replace --where="update_time>'2026-10-09 15:00'" > fix_orders.sql
  3. mysql -h生产 -uroot -p orders < fix_orders.sql
复制代码

导完按步骤 0 的口径重新 COUNT 对账,与业务方确认一致后 SET GLOBAL super_read_only = OFF,恢复写入。全程 42 分钟。

四、实操检查清单


  • 日常三参数到位:log_bin=ON、binlog_format=ROW、binlog_row_image=FULL,纳入巡检
  • binlog 保留时长(binlog_expire_logs_seconds)至少覆盖两个备份周期,且备份盘上另存一份
  • 每日全备(或全备+增量),且每月做一次真实恢复演练——没演练过的备份等于没有备份
  • 订正脚本上线前在测试库跑一遍,生产执行必须带 LIMIT 试跑 + SELECT 预览影响行数
  • 应用与运营账号只授 DML 权限,DROP/TRUNCATE/ALTER 收归 DBA 账号
  • 服务端 sql_safe_updates=ON,禁止无索引条件的 UPDATE/DELETE 直接执行
  • 预案文档写清三件事:临时实例端口、恢复命令模板、业务对账人,出事时不用现想


几个容易踩的坑

坑一:STATEMENT 格式发现太晚。 闪回工具全部依赖行镜像,STATEMENT 下只能全量重放,恢复窗口从分钟级变成小时级。这是巡检项,不是事故项。

坑二:stop-position 落进了误操作事务中间。 重放边界差一个事务,就会把脏数据也重放进去。定位时以 XID 为界,宁可少重放一个正常事务,再单独补。

坑三:DDL 误操作指望闪回。 DROP 了表找 binlog2sql 是找不到行数据的,只能全量恢复 + 重放到 DDL 前。反过来这也说明:大表 DDL 前先手动备份一次,价值就在这里。

坑四:在主库直接跑回滚 SQL。 临时实例验证通过后再单表导回,是这条流程的底线。

坑五:binlog 已过期清理。 误操作发生在一个备份周期之前,而对应 binlog 已被清掉,中间那段就永远补不回来了。保留时长必须大于"最长可能的事故发现时间"。

治理前后对比

指标治理前治理后
恢复总耗时手工逐条捞 6 小时以上42 分钟
数据找回缺 3 小时正常订单0 行丢失
误操作止血47 分钟后才设只读预案明确,5 分钟内只读
恢复演练从未做过每月 1 次,流程走通
订正脚本管控直连生产随便跑试跑+LIMIT+审计,双人复核


误删恢复这件事,功夫全在事前:ROW+FULL、备份可恢复、权限收敛、演练常态化。事故当天能做的,只是把练过的动作再走一遍。
免责申明1、欢迎访问本站,本文内容及相关资源来源于网络,版权归版权方所有!本站原创内容版权归本站所有,请勿转载!
2、本文内容仅代表作者观点,不代表本站立场,作者自负,本站资源仅供学习研究,请勿非法使用,否则后果自负!请下载后24小时内删除!
3、本文内容,包括但不限于源码、文字、图片等,仅供参考。本站不对其安全性,正确性等作出保证。但本站会尽量审核会员发表的内容。
4、如本帖侵犯到任何版权问题,请立即告知本站 ,本站将及时删除并致以最深的歉意!客服邮箱:admin@dbabbs.com
您需要登录后才可以回帖 登录 | 用户注册

本版积分规则

QQ|Archiver|小黑屋|DBA论坛中国 ( 鲁ICP备20017503号-2 )

GMT+8, 2026-10-8 06:17 , Processed in 0.017382 second(s), 10 queries , MemCached On.

Powered by Discuz! X5.0

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表