记一次数据库核心对象(CON$表I_CON2索引)损坏的修复过程
上一篇帖子已经说明了问题是如何产生的,这一篇主要是把整个解决过程详细记录下来(忽略失败的步骤),以供有同样问题的同学可以借鉴下。
一:分析问题:
最初呈现给我们的错误是这样的,如下图:
[img=440,0]http://itpubpic.img168.net/forum/201402/14/1319226txpi6xpj6ptxppn.jpg[/img]
2014-2-14 13:19 上传
http://www.itpub.net/forum.php?mod=attachment&aid=OTA2NDU1fDc2Yzc3YjQwfDEzOTI0Mzc3NTB8Mjg4OTY1MjF8MTg0NTA4Nw%3D%3D¬humb=yes]下载附件 (13.81 KB)
拿这个ORA-08102去网上搜索,很快就知道了 这是一个索引建和表中的值 不符而导致的问题。
查看这个对象号为52的对象,发现是 SYS.CON$ 表中 I_CON2索引 ,这是 一个 BOOTSTRP$ 对象,而且 OBJ# 为51 ,所以这是一个 核心BOOTSTRP$ 对象,
是不能通过startup migrate 和 event 38003 重建的,所以最终只能通过BBED去修改这个块中有问题的地方。
先通过下面这个SQL,查找表和索引之间到底相差什么
sql>SELECT /*+ FULL(t1) */
owner#, NAME, con#
FROM CON$ t1
MINUS
SELECT /*+ index(t I_CON2) */
owner#, NAME, con#
FROM CON$ t;
OWNER# NAME CON#
0 _NEXT_CONSTRAINT 60009
第一次看到这个SQL时,真心佩服想到这个SQL的人,能利用这样的方法来查询表和索引之间的不一致 ,
通过查询结果 确实可以看到 表和索引存在不一样的,就是CON$表中有 CON#=60009的这一列,而索引的键值中却没有(要知道索引的键值就是保存该索引对应的字段值啊)
我们再去看看alter日志:
因为这个报错会有日志信息显示在alter日志中,所以现在去查看alter日志:
Errors in file /http://www.itpub.net/tree/index_1/]oracle/diag/rdbms/hollycrm/hollycrm/trace/hollycrm_ora_3795.trc:
Fri Feb 14 12:09:24 2014
Trace dumping is performing id=[cdmp_20140214120924]
在去查看这个trace文件,立刻能看到这个错误地方:
oer 8102.2 - obj# 52, rdba: 0x00419ff1(afn 1, blk# 106481)
kdk key 8102.2:
ncol: 1, len: 5
key: (5): 04 c3 07 01 0a
mask: (2048):
该错误也明确,错误对象为52 , 在文件1 ,块106481 中,然后看 key的值,
第一个16进制数 04不知道代表什么意思,后面 4个字节c3 07 01 0a 正好是60009在数据库中存储的16进制表示,
sql>SELECT DUMP(60009,'1016') FROM dual;
Typ=2 Len=4: c3,7,1,a
通过上诉的 分析 ,现在我们基本可以确定,问题是这样的:
在文件1 块106481中有一个索引条目,它应该指向CON$表中 CON#=60009 这一条记录,
这个索引条目 的索引键 应该是 60009 (对应16进制为c3 07 01 0a) ,它的ROWID应该就是CON$表上 CON#=60009 这一条记录的实际ROWID;
但是现在 这个索引条目的索引键不是60009,所以才导致了我们的问题。
二:定位问题
知道了问题所在,那现在就是定位具体错误位置的时候了,根据上面的分析,
我们首先要得到CON$表中 CON#=60009 这一行的实际ROWID:
SELECT rawtohex(r.rowid) rh,
dbms_rowid.rowid_relative_fno(ROWID) fno,
dbms_rowid.rowid_block_number(ROWID) bno,
dbms_rowid.rowid_row_number(ROWID) rno
FROM con$ r
WHERE NAME = '_NEXT_CONSTRAINT' --AND con#=60009;
rh fno bno rno
0000001C00400121000C 1 289 12
我把后面con#='60009'注释掉了,这是因为用CON#=60009直接查不出来,原因应该就是这个查询使用了I_CON2索引,而索引中没有个这个键值的条目,
ROWID的格式为:[font=Consolas,]对象号(6个字符) 文件号(3个字符) 块号(6个字符) 行号(3个字符) ,
[font=Consolas,]通过rawtohex函数可以转换为16进制,现在得到了 这条记录的 文件号,块号,行号的16进制为:00 40 01 21 00 0C,因为索引条目上保存的ROWID也是文件号,块号,行号!
现在我们得到了这个记录的实际ROWID,下一步就是去索引块里去查找了ROWID了。
我们将这个索引块dump出来:
sql>alter system dump datafile 1 block 106481;
system alter
sql>oradebug setmypid
Statement processed.
sql>oradebug tracefile_name
/oracle/diag/rdbms/hollycrm/hollycrm/trace/hollycrm_ora_13537.trc
下面我们来查看这个dump文件:
直接看叶块部分:
Leaf block dump
===============
header address 8361612=0x7f968c
kdxcolev 0
KDXCOLEV Flags = - - -
kdxcolok 0
kdxcoopc 0x80: opcode=0: iot flags=--- is converted=Y
kdxconco 1
kdxcosdc 1
kdxconro 375
kdxcofbo 786=0x312
kdxcofeo 2112=0x840
kdxcoavs 2327
kdxlespl 0
kdxlende 4
kdxlenxt 0=0x0
kdxleprv 4300784=0x419ff0
kdxledsz 6
kdxlebksz 7984
row#0[7971] flag: ------, lock: 0, len=13, dataundefined6): 00 42 71 f4 00 01
col 0; len 4; (4): c3 06 59 5f
row#1[7958] flag: ------, lock: 0, len=13, dataundefined6): 00 42 71 f4 00 02
col 0; len 4; (4): c3 06 59 60
row#2[7945] flag: ------, lock: 0, len=13, dataundefined6): 00 42 71 f4 00 03
col 0; len 4; (4): c3 06 59 61
row#3[7932] flag: ------, lock: 0, len=13, dataundefined6): 00 42 71 f4 00 04
col 0; len 4; (4): c3 06 59 62
row#4[7919] flag: ------, lock: 0, len=13, dataundefined6): 00 42 71 f4 00 05
col 0; len 4; (4): c3 06 59 63
row#5[7906] flag: ------, lock: 0, len=13, dataundefined6): 00 42 71 f4 00 06
col 0; len 4; (4): c3 06 59 64
row#6[7894] flag: ------, lock: 0, len=12, dataundefined6): 00 42 71 f4 00 07
col 0; len 3; (3): c3 06 5a
。。。。。。
row#372[2138] flag: ---D--, lock: 2, len=13, dataundefined6): 00 42 71 f2 00 1c
col 0; len 4; (4): c3 06 64 40
row#373[2112] flag: ---D--, lock: 2, len=13, dataundefined6): 00 42 71 f2 00 1d
col 0; len 4; (4): c3 06 64 41
row#374[2125] flag: ------, lock: 0, len=13, dataundefined6): 00 40 01 21 00 0c
col 0; len 4; (4): c3 06 64 42
----- end of leaf block dump -----
End dump data blocks tsn: 0 file#: 1 minblk 106481 maxblk 10648
这个索引块中一共有375行,我们随便看其中一行
row#374[2125] flag: ------, lock: 0, len=13, dataundefined6): 00 40 01 21 00 0c
col 0; len 4; (4): c3 06 64 42
这里有两个地方比较重要,一个是 dataundefined6): 00 40 01 21 00 0c ,另一个是col 0; len 4; (4): c3 06 64 42
可能在不同的版本中,这里格式不一样,我这里是11gR2的,
dataundefined6): 00 40 01 21 00 0c 这里代表的是ROWID的信息,
col 0; len 4; (4): c3 06 64 42 这里代表的是索引键的值
通过查询,很快就找到了这一行:
row#374[2125] flag: ------, lock: 0, len=13, dataundefined6): 00 40 01 21 00 0c
col 0; len 4; (4): c3 06 64 42
而我们看到这个索引条目上的索引键为 c3 06 64 42 确实不是 我们表上的 c3 07 01 0a,
索引条目算是找到了,现在就要把这个索引条目在这个块上的偏移量找到就OK了,
偏移量offset的计算方式,网上有个帖子是这么说的:
[color=#3f4549]偏移量=N+44+24*itl N是行数据的补偿 44是 kcbh(cache层)长度+ktbbh(事务层)长度 24是一个itl的长度 总共加起来其实就是行数据的offset
不过帖子里也没说是为什么,见http://www.killdb.com/2011/07/30/bootstrap核心对象数据不一致导致ora-08102.html]http://www.killdb.com/2011/07/30/bootstrap核心对象数据不一致导致ora-08102.html
我也按照这个方法计算了,得到 2125+44+24*3=2268
现在地方也找到了,只需要把这里的c3 06 64 42改为c3 07 01 0a 就OK了。
三:解决问题
最后我们通过BBED来修改这个块,
BBED> set dba 1,106481 offset 2268
DBA 0x00419ff1 (4300785 1,106481)
OFFSET 2268
BBED> d /v
File: /oracle/oradata/hollycrm/system01.dbf (1)
Block: 106481 Offsets: 2268 to 2779 Dba:0x00419ff1
-------------------------------------------------------
40012100 0c04c306 64420100 004271f2 l @.!...?.....Bq?
001c04c3 06644001 00004001 21000c04 l ...?d@...@.!...
c3066441 01000042 71f2000d 04c30664 l ?dA...Bq?..?d
3f010000 40012100 0c04c306 64400100 l ?...@.!...?d@..
004271f2 002004c3 06643e01 00004001 l .Bq? .?d>...@.
21000c04 c306643f 01000042 71f2001e l !...?d?...Bq?.
04c30664 3d010000 40012100 0c04c306 l .?d=...@.!...?
......................................
21000c04 c306642f 01000042 71f20020 l !...?d/...Bq?
<16 bytes per line>
可以看到,这里就是我们要找的地方了,然后我们就是要修改红色部分,
BBED>modify /x c307 offset 2274
输出略
BBED>modify /x 0101 offset 2276
输出略
我们再看一下这个地方:
BBED> d /v
File: /oracle/oradata/hollycrm/system01.dbf (1)
Block: 106481 Offsets: 2268 to 2779 Dba:0x00419ff1
-------------------------------------------------------
40012100 0c04c307 010a0100 004271f2 l @.!...?.....Bq?
001c04c3 06644001 00004001 21000c04 l ...?d@...@.!...
c3066441 01000042 71f2000d 04c30664 l ?dA...Bq?..?d
3f010000 40012100 0c04c306 64400100 l ?...@.!...?d@..
004271f2 002004c3 06643e01 00004001 l .Bq? .?d>...@.
21000c04 c306643f 01000042 71f2001e l !...?d?...Bq?.
04c30664 3d010000 40012100 0c04c306 l .?d=...@.!...?
......................................
21000c04 c306642f 01000042 71f20020 l !...?d/...Bq?
<16 bytes per line>
BBED> sum
BBED> sum apply
最后刷新下数据库的缓冲区
SQL>alter system flush buffer_cache;
再次创建索引,约束时,不再报错了。
以下是老白大神(百鳝)的一篇有用的帖子
http://www.docin.com/p-277739036.html]http://www.docin.com/p-277739036.html
(注:此贴是我在ITPUB上发表的,由于注册此网站需要发一篇帖子才能成为正式会员,所以先拿过来了。)