演练比事故便宜。这篇记录一次「假设有人执行了不带 WHERE 的 DELETE」的完整恢复过程,全程在测试库做。
场景设定
凌晨 2 点,有人执行:
DELETE FROM orders; -- 忘了 WHERE
3 万条订单消失。备份是每天 3 点的全量 mysqldump,binlog 保留 7 天。
恢复思路
全量备份(昨天 3 点) + binlog 回放(昨天 3 点 → 误删前一刻) = 恢复
关键是找到误删的那条 binlog 位置,回放到它之前。
第一步:确认 binlog 开着
SHOW VARIABLES LIKE 'log_bin'; -- 必须是 ON
SHOW VARIABLES LIKE 'binlog_format'; -- 必须是 ROW
SHOW BINARY LOGS;
binlog_format=ROW 很重要,它记录每行数据的变更前后值,能精确恢复;STATEMENT 格式记录 SQL,遇到 NOW()、RAND() 这类函数会不一致。
第二步:找出误删位置
mysqlbinlog --base64-output=decode-rows -v mysql-bin.000123 \
| grep -B5 -A20 "DELETE FROM orders"
输出里能看到 DELETE FROM orders 所在的 at 位置,以及它是从哪个位置开始的。记下误删语句之前的 position。
更快的办法是按时间过滤:
mysqlbinlog --start-datetime="2024-12-14 01:50:00" \
--stop-datetime="2024-12-14 02:10:00" \
mysql-bin.000123 | less
第三步:恢复到临时库
不要直接往生产库灌,先恢复到临时实例验证:
# 1. 恢复昨天的全量
mysql -u root -p temp_restore < backup/wordpress-2024-12-13.sql
# 2. 回放到误删之前
mysqlbinlog --start-position=4 --stop-position=1234567 \
mysql-bin.000123 | mysql -u root -p temp_restore
第四步:校验
SELECT COUNT(*) FROM temp_restore.orders;
SELECT * FROM temp_restore.orders ORDER BY id DESC LIMIT 10;
和业务方核对最后几条记录,确认数据完整。
第五步:只补回缺的部分
全量覆盖生产库意味着这段时间的新数据也会被回滚。更稳的是只把缺失的行插回去:
-- 找出生产库缺的 id
SELECT o.* FROM temp_restore.orders o
LEFT JOIN prod.orders p ON o.id = p.id
WHERE p.id IS NULL;
-- 用 INSERT ... SELECT 补回
如果误删之后还有更新,情况会更复杂,要按 binlog 逐条重放。这时候通常建议停服、整库恢复、再补增量。
预防
- 权限:业务账号不给 DELETE,删除走软删(
is_deleted标记) - SQL 审核:
DELETE/UPDATE必须带 WHERE,工具层拦截 - 延迟从库:
CHANGE MASTER TO MASTER_DELAY = 3600,误删后一小时内可以从延迟从库拿到原始数据 - binlog 保留至少 7 天,而且要异机备份——这次演练特意检查过,binlog 原本只留 3 天
结论
恢复能成功的前提是:binlog 开着、备份可用、知道怎么找 position。这三样平时不起眼,出事时决定生死。建议每个管数据库的人都真跑一次这个演练。
- THE END -
最后修改:2024年12月14日
非特殊说明,本博所有文章均为博主原创。
如若转载,请注明出处:https://mi-blog.cn/archives/690
共有 0 条评论