背景
在系统运营中,经常会有一些业务变更,需要直接更新表中的一些数据。最近我们遇到了一件棘手的生产事件,开发同事本来要更新表中的很少一部分数据,但是由于UPDATE SQL编写问题,将整个表的一个字段进行了更新。SQL大概是长这样的:
开发同事原意是希望根据tab_tmp b表的字段更新tab_order a 表的字段status值为'01',但是由于exists里面不应该再次出现tab_order c导致子查询恒为真,全表的字段被更新。这么大量的表数据更新,将会导致不低于删库跑路的严重后果。
有几个信息同步一下:
- 数据库是Oracle。
- tab_order 有1亿行数据,表大小30G。
- undo表空间30G左右。
- 更新动作执行了40分钟。
处理方案
处理误更新数据我们有几种方式:
- 事务未结束,直接rollback
Oracle的事务是手动提交的,可能有挽回余地,因为修改的数据量较多,回滚时间会比较长。其它类型数据库大部分是自动提交的,我们可以通过显示打开事务的方式进行操作。
2.使用闪回查询
如果你的事务已经提交了,那么可以优先考虑闪回查询恢复。当数据库undo表空间足够,undo_retention保留时间足够长,是可能会查到修改前的原数据的。
闪回查询真的很好用,目前oracle、tidb、oceanbase等数据库有这个功能的。
3.使用LogMinner挖掘日志,执行undo sql恢复
如果闪回查询无法查询到数据,报错ORA-01555,而且能准确知道修改了多少行数据的情况下,可以优先考虑LogMinner恢复原数据。LogMinner主要依托于挖掘DML SQL执行期间生成的redo log中的原值来恢复数据,在LogMinner挖掘后日志后会看到 undo sql(回滚sql)可以用来直接恢复数据。
Mysql 的binlog 中记录了新旧值,需要通过工具生成回滚SQL。
4.使用备份恢复异机恢复表,导出、导入恢复
本次恢复由于几个方面因素不满足,我们使用了最终的异机备份恢复的方案。异机恢复过程中,涉及的表空间较多,为了快速恢复表,我采取了部分恢复的方式,加速了表导出的过程。
总结
上面的4种方案,由上到下依次恢复成本增加,相关经验可以参考借鉴。
札记:“成长的经历会告诉你:每天早起五分钟,人生将被赋予另一种可能!每天多付出一点努力,一天不见回报,一个月不见回报,一年不见回报但十年一定会有所回报的”。
--《哈弗凌晨四点半》