出现Ora-8102的原因一般是由于索引中的KEY和TABLE里的相关字段值不同导致数据不一致引起。一般来说,出现ORA-8102,是由于数据库逻辑或者物理故障引起的,损坏的可能是表数据,也可能是索引数据。
如果损坏的是索引数据,那么只需要将索引重建就可以使表和索引数据一致,从而解决问题。如果损坏的是表数据,那么要看损坏的范围,如果只是损坏了某一行,那么纠正某一行的数据就可以了,如果损坏的面积较大,那么处理起来就比较复杂了。Oracle有一份文档1034886.6就是解释如何处理ORA-8102的,文章涉及的数据库版本比较老(7版),不过基本思路大致是没有问题的。针对较大面积的损坏,在1034886.6里面也没有给出很好的处理方案,绝对是十分麻烦的事情,不过有一种变通的方案,导出数据,然后重建对象是最佳的选择。
不过有一种情况十分头痛,如果损坏的表不是简单的用户表,而是一个字典表,如果更加不幸,碰到了核心BOOTSTRAP$对象,那么就十分头痛了。前几天客户那边出现一个十分奇怪的现象,就是使用IMP导入数据的时候碰到ORA-8102错误,错误是在创建表的时候发生的:
SQL> create table tcon2( a integer not null);
create table tcon2( a integer not null)
*
ERROR 位于第 1 行:
ORA-00604: 递归 SQL 层 1 出现错误
ORA-08102: 未找到索引关键字,obj# 49,dba 4277961 (2)
尝试随便创建一张表:
create table test( a integer);
是能够成功的。查找OBJ#=49的对象,发现是I_CON2索引出现问题。该索引是CON$上的一个索引,CON$是维护约束关系的。通过TRACE可以看到:
*** 2008-08-08 16:56:58.984
*** SESSION ID:(12.7) 2008-08-08 16:56:58.984
oer 8102.2 - obj# 49, rdba: 0x004146c9
kdk key 8102.2:
ncol: 1, len: 4
key: (4): 03 c2 3d 02
表中的值是C2 3D 02 (6001),通过TRACE查看索引的值:
BH (0x653EFC74) file#: 1 rdba: 0x004146c9 (1/83657) class 1 ba: 0x65273000
col 0; len 2; (2): c2 3d
row#135[632] flag: -----, lock: 0, data:(6): 00 40 01 52 00 0c
col 0; len 3; (3): c2 3d 05
索引中的值是c2 3d 05(6004),就是由于这个原因导致。一般情况下如果表没有损坏,那么通过REBUILD索引可以解决问题。但是49号对象是核心BOOTSTRAP$对象,是不能REBUILD的:
SQL> alter index sys.i_con2 rebuild;
alter index sys.i_con2 rebuild
*
ERROR 位于第 1 行:
ORA-00701: 无法改变热启动数据库所需的对象
这种情况,为了解决不一致,只能使用BBED修改索引或表中的值,使之能够一致。
BBED>set file 1
BBED>set block 83657
BBED>modify /x 0x05 735
BBED>sum apply
然后重启数据库,发现故障已经解决。