误删库后可用延迟从库的relay log恢复,需先确认Seconds_Behind_Master为正、SQL_Delay>0且relay log文件存在,再STOP SLAVE SQL_THREAD,通过GTID或Relay_Log_Pos定位误操作前位置,用UNTIL指令回放,最后mysqldump导出并关闭binlog导入主库。

确认从库relay log是否完整可用
误删库后想用relay log恢复,前提是目标从库没跟着一起同步删除——也就是说,它必须是延迟从库,或者在误操作发生前已停止SQL线程。如果主从实时同步且未设延迟,那从库的relay log里大概率也包含了DROP DATABASE语句,直接应用会重蹈覆辙。
先登录从库检查状态:SHOW SLAVE STATUS\G,重点关注以下三项:
-
Seconds_Behind_Master:如果是正数(比如3600),说明有延迟,数据可能还“活”着 -
Relay_Log_File和Relay_Log_Pos:记录当前SQL线程执行到哪条relay log事件,别让它越过误操作点 -
SQL_Delay:值大于0才真正构成延迟保护,否则只是心理安慰
再确认relay log文件本身是否还在磁盘上:ls -l /var/lib/mysql/relay-log.*。如果被RESET SLAVE或FLUSH LOGS清过,就只剩binlog可用了。
停止SQL线程并定位误操作前的GTID或位置
一旦确认relay log可用,立刻停掉SQL线程防止自动执行到误删语句:STOP SLAVE SQL_THREAD;。这步不能跳,否则几秒内数据就没了。
接下来要精准卡在误删之前的位置。推荐优先用GTID(MySQL 5.6+且开启gtid_mode=ON):
- 查误操作发生时的GTID:
mysqlbinlog --base64-output=DECODE-ROWS -v /var/lib/mysql/relay-log.000001 | grep -A5 -B5 "DROP DATABASE mydb" - 找到对应
SET @@SESSION.GTID_NEXT = 'xxx:yyy';那一行,记下xxx:yyy - 用
SELECT GTID_SUBTRACT(@@global.gtid_executed, 'xxx:yyy');验证该GTID确实在已执行集合里
如果没开GTID,就得靠位置(Relay_Log_Pos)手动推断:翻看SHOW BINLOG EVENTS IN 'relay-log.000001'输出,找DROP DATABASE前一条Query事件的End_log_pos值。
用START SLAVE SQL_THREAD UNTIL精准回放
拿到截止点后,在从库上执行回放指令,只跑完误删前的所有变更:
- GTID模式:
START SLAVE SQL_THREAD UNTIL SQL_BEFORE_GTIDS = 'xxx:yyy'; - 位置模式:
START SLAVE SQL_THREAD UNTIL RELAY_LOG_FILE = 'relay-log.000001', RELAY_LOG_POS = 123456;
注意不是START SLAVE全开,必须限定SQL_THREAD且带UNTIL。执行后等Seconds_Behind_Master变成NULL、Slave_SQL_Running_State显示Slave has read all relay log才算成功停住。
此时从库数据已回到误删前状态,但仅限于该从库实例——它还没对外提供服务,也没和主库重新同步,所以不会影响线上业务。
导出数据并安全还原到主库
恢复动作到这里只是“看到数据”,还没真正救回来。下一步是把从库里抢救出的库导出来,再灌回主库:
- 用
mysqldump --databases mydb > mydb_recover.sql导出(加--single-transaction避免锁表) - 主库上先创建空库:
CREATE DATABASE IF NOT EXISTS mydb; - 导入前务必关闭主库binlog写入:
SET SQL_LOG_BIN = 0;,否则导入操作又会被记进binlog,可能触发二次同步污染 - 执行
source mydb_recover.sql,完成后SET SQL_LOG_BIN = 1;
最后一步容易被忽略:导出时若表含自增主键或外键约束,mysqldump默认不带--skip-triggers和--disable-keys,可能在导入时因触发器或索引重建失败。建议加上这两个参数保底。


















