记一次ora600[12700]的处理

环境大了,情况复杂,需要考虑的事情就多了。前几天遇到个5节点rac做parmary+3节点rac做pyhsical dataguard的环境,在primary的某个节点,由于突然的宕机,当我们把它启动重新加入cluster后,就出来了很多问题。这次故障不是我处理的,是我的几位同事一起处理的,从12月14号开始就一直追这个case,期间跑analyze table validate structure cascade online,从14日开始跑一直跑了十多天还跑不完,期间和oracle support也多次打交道。等处理完毕后,同事发邮件和大家共享了这个case。我也觉得这个case挺有意思,在这里按照我的理解,再次整理了一遍这个case的思路,写成博文和大家共享一下。

事情的起因是这样的,在12月14日的时候,同事收到告警,说某rac的节点2宕机了,登录后发现该数据库已经down库,且相关的bdump,cdump,udump目录都不见了,在恢复业务为第一要务的情况下,新建了目录,重启了数据库。但是在重启之后的几个小时,数据库开始报错了,先是ora-600[kclchkblk_3],再是ora-600[12700],并且有ORA-00600 [6122],后续的,还在standby上还发生ORA-600 [3020]。

我们按照时间,先看看600 error出现经历:

我们看到,问题的起因是因为数据库由于某种原因,数据库重启了,目前该原因已经无法分析,因为bdump等目录在重启前已经被删除(或许有可能是被谁删除了该目录才导致宕库的,但是,也无法追查),在数据库重启之后,引发了ora-600[kclchkblk_3]的报错,该报错是后续所有报错的起因。而该报错,经过oracle确认,是由于Bug 4767885: “OERI [kclchkblk_3] possible in RAC”引起的,这个bug在9208下没有fix,但是该bug的后果却很严重,会导致写丢失(lost write)。在本案例中,由于复杂的架构,你会看到该bug引起了很多问题。

在正常的情况下,oracle会把所有的更改信息写入redo log,在checkpoint时也将dirty buffer中的内容刷入到数据文件中。但是由于ora-600[kclchkblk_3],写丢失,某个块中的某些行没有写入(下文的dump的文件中,你会看到file#562,block#131248中的第五行丢失),于是就导致报错出来了ora-600[12700]。

ora-600[12700],简单的说是rowid指向了一个不存在的数据块,即索引中的slot是包含rowid信息,然后根据rowid去找表中的数据块。当出现rowid无法找到其对应的数据块时,就抛出了ora-600[12700]。

根据ora-600[12700], [542490], [2357330096], [5], [0], [24], [], []的报错,我们在metalink上找到12700后面几个参数的含义。

根据metalink上的指示,对于该错误我们的做法是重建索引或者修复坏块。
我们根据a,data_object_id去找该报错所对应的对象:

从data object id上看,这个对象似乎是在表上M_IMPOR,是和数据的坏块,但是接下去的检查,我们发现,他是在索引上的。(我们注意到,这里的object_id和data_object_id已经不一样了。)

用上述的方式检查,我们也用b,通过RDBA去找对象是什么:

我们看到也是同样的表。

那么我们再看看该问题所指的rowid是什么?我们用dbms_rowid,将data_object_id,file_id,block_id,和row代入进去。

但是此时如果用select * from SBUXXE.M_IMPOR where rowid=AACEcaAIyAAAgCwAAF就会触发ora-600[12700]的报错。因为该rowid指向的数据是不存在的。

除了上述的检查,我们还能从trace文件中找到相关的信息:

上述的2种方法去找问题都是可以的。

综上所述,有个rowid为AACEcaAIyAAAgCwAAF需要指向在file id为562,block id为131248的块上的第5个slot的这一行,而file id为562,block id为131248的块上的第5个slot的数据丢失。即rowid AACEcaAIyAAAgCwAAF指向一个空块。

打个比方,就像一群人拿着名片,上面写着IBM公司在第562号大街上的第131248号大楼的第五层,而当他们按图索骥的去到那里,发现第五层根本没有公司在那里,是一层空屋子而已。

那么究竟是名片印错了,还是大楼第五层的房子被强拆了?我们开始检查:

我们用dbv检查:

没有发现数据上的坏块,也就是说,我们从工商局发现那个地方确实没有公司注册在那里,那个空层是正常的,错误的是名片印错了。我们要来纠正名片上的错误。

由于该表上有73个索引,大约120多G,开始运行analyze cascade online,由于transaction也很多,所以跑起来很慢很慢了,运行了十几天没跑完。这里我们先放着它跑,来看其他的问题。

在跑的时候,物理的DG数据库又报错了ORA-00600: internal error code, arguments: [3020], [2357330096], [5], [27066], [891734], [76], [], [],表明在RDBA为2357330096的块的第五行上出现问题,redo上的信息和用于recover的块上的信息不一致,引起了所谓的“stuck recovery”。也就是说,在Primary中,在触发ora-600[kclchkblk_3]时,相关的信息虽然没有写入到RDBA为2357330096的块的第五行上,但是却成功的写入到了primary的redo上(因为先写redo,再在checkpoint的时候刷到数据块中),该redo被传输到physical datagaurd上,并且被apply,且成功apply,修改了在physical dg上的RDBA为2357330096的块的第五行上。当主库再次更新RDBA为2357330096的块时(可能是除第五行外的其他9行),primary将该redo的信息传输到physical dg,发现redo上的信息和在dg上要做recover的块上的信息不一致,于是报错了ora-600[3020]了。

对于这个问题,我们将主库的该文件至于热备模式,拷贝到备库,通过这种方式达到主库备库的文件一致,来修复dg。并且,在操作的时候,幸运的是我们的同事先备份了dg上的该文件,再把拷贝过来的文件覆盖。为什么备份重要?后续的故事你会听到……

好了,我们的故事继续,在做analyze cascade online的时候,application team发现他们的应用中,有个M_IMPOR的子表——M_IMPOR_CHIL表的数据已经存在了,但是父表,即M_IMPOR表却没有对应的数据。

为什么会造成这样的现象?不该子表有数据父表没数据啊?难道是遇到了更大的bug?

通过检查发现,发现子表和父表建没有建立约束关系,仅仅是存储数据罢了,因此oracle在lost write父表的时候,还是写入了子表。

但是由于父表的数据非常重要,必须得找回这条丢失的数据,我们又要开始想办法了。

直接通过select * from M_IMPOR where rowid=AACEcaAIyAAAgCwAAF得到的返回值是空,我们如何得到rowid为AACEcaAIyAAAgCwAAF的相关的值呢?

我们知道,在索引中,不仅仅存储了rowid,还存储了键值,在执行计划走索引的情况下,会直接读取键值,而不会根据rowid再去读表中的数据块。

因此我们将通过强制走索引的方式,来获取索引上存储的值,希望通过这些值能还原表的数据,或者说,希望能通过这部分的数据,帮助application team找到线索去还原数据,毕竟,表有248个字段,索引才73个。

我们知道,在当前的数据库中,该表的索引存在2种状态,一种是rowid=AACEcaAIyAAAgCwAAF不包含数据的。一种是在rowid=AACEcaAIyAAAgCwAAF上有数据的。前者用强制索引查询,会返回0行,后者强制索引查询,能返回该索引块上的数据,即存储的键值,如果强制用rowid去找表的记录,将返回ora600[12700]

我们用hints的方式直接读取索引中的键值:(以索引M_IMPOR_M9_X为例)

有一个索引字段,是建立在表的USEDATE的字段上,我们看看用hints的执行计划,注意我们这里只选择了被索引的USEDATE字段,而没有选其他字段:

发现是走index fast full scan的,而不是走通过TABLE ACCESS BY INDEX ROWID找表上的块的数据。

因此,当我们运行:

的时候,可以直接从索引中,而不经过表中,取得该索引的键值:

以此类推,我们可以获取其他72个索引的值。

不过遗憾的是,由于为了减少ora-600[12700]的报错,之前由于索引ananlyze cascade online一直出不来,就停止了analyze脚本,直接重建了部分索引了。因此,重建后的索引,已经不会报ora-600[12700],但是却已经无法取到索引上的键值。怎么办?

上文中已经提到了,同事在physical dg上处理ora-600[3020]的时候,在覆盖数据文件之前,先备份了一个数据文件。注意,这个被备份的数据文件,是已经redo apply成功数据文件,它上面有完整的块信息。因此,我们只要从这个备份中读取数据,就能把数据提取出来,然后在主库插入该记录即可。怎么提取?我们便用到了分析block的利器BBED:

BBED的e(x)amine可以用来显示整行数据的信息:

注意,这个表有248个字段但是只导出了230个字段,因为230个字段后顿时null,。所以bbed导出的时候,会不记录这些null值。

至此,我们已经完全得到了丢失的数据,后续要操作的工作,就是把该表相关的索引都重建一次(注意这里的重建,不是指rebuild,而是drop and create),把丢失的数据交给application team,让其在主库插入该数据。

参考文档:
metalink Doc ID 28229.1
metalink Doc ID 155933.1

相关文章

3条评论

发表评论

电子邮件地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据