前两天在一技术群里围观了一场云上数据库误删库(drop schema)的故障,场面一度很是热闹...
![](https://oss-emcsprod-public.modb.pro/wechatSpider/modb_20220515_25b49350-d3ee-11ec-89bc-38f9d3cd240d.png)
巧的是,作者近期工作中也恰好遇到了一起因开发编写的归档条件错误导致了数据误删故障的案例,一时间,有点心有戚戚了。
![](https://oss-emcsprod-public.modb.pro/wechatSpider/modb_20220515_25c9f7e0-d3ee-11ec-89bc-38f9d3cd240d.png)
虽然平时开发或者DBA平时也会调侃着“删库跑路”的话,但是当“删库”这种窒息操作发生了,尤其这事儿还落在本人身上时,当事人的心情恐怕就没那么美好了。
言归正传,所以,postgresql误删数据了,要直接跑路吗?
作者答:我觉得不要“直接”跑路吧,我觉得还可以抢救一下(狗头.pic)。
![](https://oss-emcsprod-public.modb.pro/wechatSpider/modb_20220515_25da40c8-d3ee-11ec-89bc-38f9d3cd240d.png)
当故障已发生,我想第一步肯定是要先设法恢复误删数据了。
顺嘴说一句:删库了,还是先别急着在技术群里问陌生人了,不是说不可以,只是效率不高,大家七嘴八舌的,还要筛选哪些是真正有用的。自建数据库找公司DBA,云上数据库就找运维支持,第一时间联系最能有效解决问题的人,别拖,拖得越久,恢复越久......
回到正题,PG需要怎么恢复误删数据呢?
一般情况下,就是从备份中恢复数据或者利用PITR。
首先是备份,备份可以有SQL Dump或文件系统级别的备份。但是能从备份中恢复数据的前提是在做删除操作之前,有做过备份操作。
再看PITR, 其全称是Point-In-Time-Recover (时间点恢复),是PG从8.0版本开始引入的一个特性,该特性可以使用基础备份和连续归档日志将数据库集群恢复到任意时间点。(有关PITR的更多内容,可以参考我之前的文章:PostgreSQL之PITR)
PITR工作需要两个重要的先决条件:
基础备份的可用性。 连续归档日志。
关于PG的备份和恢复可参考官方Chapter 26. Backup and Restore,有详细介绍。
幸运的是,我司误删数据的实例(我们是mysql,但是性质一样的),有基础备份,也有做日志归档。所以最终数据还是恢复出来了。但是貌似群里那位同志的实例是基础版,不支持日志备份,没办法做按时间点恢复。并且最近一次全备时间还是前一天,也就是说要丢一天的数据(不考虑实例下wal日志未被覆盖的情况)。这就非常悲催了......
看完热闹之后,后来我也在想如果是像群友这种情况,数据是不是就真没有办法恢复了呢?
我在stackflow上看到一篇高赞回答,提了一种思路。我没有实践过,但觉得还是有道理的。 链接:https://stackoverflow.com/questions/12472318/can-i-rollback-a-transaction-ive-already-committed-data-loss
1.首先要立即停止数据库,不要重新启动它。而且要使用pg_ctl stop -m immediate,这样在关机时不会运行checkpoint。
2.数据库停止后,应该在文件系统级复制整个数据目录—包含base、pg_clog等的文件夹。将所有内容复制到新位置。不要对新位置的副本做任何操作,如果没有备份,这是恢复数据的唯一希望。
3.做好备份之后,一些可能可以恢复数据的方法:
1)利用MVCC的机制: 如果可以足够快地停止数据库,还是可能有希望从表中恢复一些数据。这是因为PostgreSQL使用了多版本并发控制(MVCC)来管理对其存储的并发访问。有时,它会将您更新的行写入新的版本,而将旧的保留在原地,但标记为“已删除”。过了一段时间,autovacuum出现并将行标记为空闲空间,这样它们就可以被稍后的INSERT或UPDATE覆盖。因此,UPDATED行的旧版本可能仍然存在,但不可访问。
2)此外利用wal机制:pg的写分为两个阶段。首先写入WAL (write-ahead log)。只有当它被写入WAL并命中磁盘时,它才会被复制到“堆”(主表)。WAL内容由bgwriter和定期checkpoint复制到主堆。缺省情况下,检查点每5分钟出现一次。如果我们能在操作之后,下一次checkpoint之前,设法停止数据库。旧的数据更可能仍然是在堆中。
我觉得思路是可行的,但操作起来有难度。比如第一种方案中,怎么获得不可见行是个问题,答主提供了一种pg_dirtyread的工具,不知道是否可用,见:https://github.com/omniti-labs/pgtreats/tree/master/contrib/pg_dirtyread。
另外上面提到的两个方案无论是利用MVCC还是WAL机制,都要求在故障发生后,在尽可能快地停止数据库,备份出整个数据目录。
所以说,误删操作发生后,一定要第一时间作出响应。不说这种无法使用归档日志的情况,即使有归档日志可以做按时间点恢复。怎么追平恢复时间点到当前时间这段时间的数据,时间越久越困难。
Anyway,“以人为镜可以明得失”,就是从这件事中,还是可以从中吸取一些经验教训的。
说说我自己能想到的点吧。
做好权限控制,高危操作的权限一定要控制好,像drop、truncate这种权限,一定不能随意分配,不然什么都有可能发生...。
不要贪便宜用单节点基本版...除非你的业务不重要,或者能接受数据丢失和服务不可用。
备份很重要。 数据库一定做基础备份+归档,或者是延迟备份。
设置合理的备份策略,对于一些重要的业务,数据库的备份频率不能太低了,否则发生数据恢复时间很长的。
最后,数据库的问题,我觉得预防永远比治疗更重要,出现问题后,再去解决问题,只能说是去止血,业务已经伤了。
所以说,调侃归调侃,希望大家都不会有“删库跑路”一天。
![](https://oss-emcsprod-public.modb.pro/wechatSpider/modb_20220515_25eec426-d3ee-11ec-89bc-38f9d3cd240d.png)
参考:https://stackoverflow.com/questions/12472318/can-i-rollback-a-transaction-ive-already-committed-data-loss
点个“赞 or 在看” 你最好看!
👇典典下面的小咔片给作者鼓励下吧,这对我很重要🐶