love wife love life —Roger的Oracle/MySQL/PostgreSQL数据恢复博客

Phone:18180207355 提供专业Oracle/MySQL/PostgreSQL数据恢复、性能优化、迁移升级、紧急救援等服务

如何拯救一个10多年的老库

本站文章除注明转载外,均为本站原创: 转载自love wife love life —Roger的Oracle/MySQL/PostgreSQL数据恢复博客

本文链接地址: 如何拯救一个10多年的老库

前几天一个朋友说他们的数据库存储坏了,但是存储修好之后,数据库无法启动了,让我帮忙诊断一下。想想自己已经很久很久没处理过这么复杂的Case了,简单记录分析一下。就当怀旧吧,毕竟这么老的环境也不多见了。

首先看alert log,发现最早的记录是2010年3月26号。 想想2010年的自己,还是一个年轻羞涩的帅小伙!   时光如梭!已经10多年过去了。。。

我们继续看数据库启动时刻的报错:

作为技术经验比较老道的老司机,一眼就知道这是控制文件损坏了,大家网上应能搜到不少的案例,如果我们去查官方解释,那么是这样的:

ORA-600 [kccpb_sanity_check_2] indicates that the seq# of the last read block is
higher than the seq# of the control file header block. This is indication of
the lost write of the header block during commit of the previous cf transaction.

问用户说他们没有数据库备份(实际上数据库是非归档),那么既然是这样,Oracle官方推荐的利用备份恢复控制文件的方法肯定是行不通了。

那我们先来人工重建数据库控制文件吧。对于重建控制文件的脚本,大家都知道,其有几个关键的点:dbname、数据库是否归档、字符集。前面2个信息很容易从数据库alert log去获取。那么数据库字符集呢?数据库都无法打开,自然是无法去查数据库视图的。

以前如果学过我备份恢复课程的同学一定知道,我之前讲过,Oracle的字符集等信息存储在props$这个数据库字典表中,而在Oracle 10g版本中,该字典表存储在file 1 block 722 block中。因此我们可以这样来获取字符集信息:

可能是很久很久没玩过裸设备的环境了,发现上面的命令居然不行,然后我搜索alert log发现,居然能看到10多年的数据库创建信息:

当我们知道了这些信息之后,就可以编辑重建controlfile的脚本了。从上面的信息,我们可以看到,使用的是裸设备;从2010年至今,这套Oracle rac环境运行时间超过了13年了(hp unix serviceGuard做的集群)。想想用户提供的这个alert log居然就10Mb不到,居然存了13年的告警信息;想到这里,我再回想一下OceanBase的log,简直头大。创建控制文件的脚本如下:

创建完控制文件之后,此次此刻就可以Mount数据库实例了。然而我查下了下v$datafile /v$datafile_header发现checkpoint_change#差别非常大,高达数百万之多。

这种无备份,无归档的情况之下,scn差距如此之大,基本上只能去强制打开数据库了。我尝试使用recover database using backup controlfile until cancel 恢复之后;打开数据库时报了如下错误:

很明显Oracle在open的时刻,进行bootstrap初始化就失败了。从报错的ORA-00600 kcbgtrc来看,这是在构造一致性读。说明该过程中读到了一些异常的数据块。

要解决这个问题也不难,尝试禁止SMON 回滚(使用event 10510/10511/10512),并结合_allow_corruption_resetlogs、undo_management=manual参数即可打开数据库;然后重建undotbs1/undotbs2 即可,并add tempfile reuse。

本以为很容易就打开数据库了,应该没啥事儿了,可以收工了。习惯性尝试create table test,虽然能进行dml,但是无法commit也无法进行drop table操作。此时去看alert log会发现类似的一些错误:

从上述的错误我们大致就知道,必然是有一些数据字典表和索引不一致了。对于这类错误,我们可以通过如下操作来分析,观察具体在哪里不一致。

这下麻烦了。Oracle最核心的obj$出问题了,另外看日志还发现dependency$ 的索引也异常了。对于dependency$ 而言,处理相对简单,很早 之前在博客(www.killdb.com)写过使用bbed来处理的方法。实际上数据库如果能open的情况之下,那么可以直接drop 并重建dependency$上面的2个索引即可(设置event 38003 并重启实例后可以操作)。

然而对于obj# <56 的这部分核心数据字典对象,是无法通过event 38003来处理的。我们来看看analayze分析的结果:

很明显,这里已经提示我们,row mismatch了,而且obj $的这个block 似乎也有点异常,还有2行lock记录,这里通过bbed直接处理掉(处理掉之后,解决了smon进程回滚遇到的一些错误,包括alert log中的一些ora-01555错误。如下所示:

为什么一定要去处理obj$,我发现上述的错误是非常奇怪的。实际上到这里,我已经把之前的undotbs1/undotbs2 都drop重建了。

这里为什么Oracle在查询的时候还会去读file 2 block 241410呢,很明显这是应该是之前的undo信息。因为此时我尝试dump这个undo block,发现是是一个未格式化的block。按理说已经处理掉执行计划中的几个表和索引,且都analyze过没问题。所以一定是问题出在obj$上。

尽管如此,改了几个block,包括将上述报错的回滚段drop掉,我发现exp数据的时候,仍然会报错ora-01555,那么大概率是读写的部分block还是有一些异常,既然如此就推一下scn(因为看alert log还发现了零星的ora-00600 2662 错误)。

最开始本想使用event 10015 来推scn,发现并不起作用,既然如此就使用oradebug poke操作吧,需要注意的是,hp unix环境的scn 高位在前面,因此poke的时候注意level(16进制)。

在恢复过程中,发现alert log陆续还有不少的ora-00600错误,比如:

以及一些跟数据坏块相关的错误。

在exp 数据的过程中,发现有些表有问题会导致数据库crash,最终还是要通过bbed将obj$的索引删除掉,会减少一些错误。然而数据字典基表的索引被删除,也会有一些副作用,比如你的递归sql查询效率会大幅降低。

我想以后很难再遇到这样的Case了,简单记录一下!

备注:我在想,如果国产数据库遇到极端的灾难情况,我们是否有一些手段来抢救数据呢?比如OceanBase、达梦、openGauss?

 

Leave a Reply

You must be logged in to post a comment.