1.故障原因
前段时间在几个医院发生几次"ora-8103 对象不存在"的错误,出现这种错误,很多情况都是bug,但也可能出现在以下几种情况: -- 执行某个sql语句时 -- 错误出现在alertsid.LOG日志中,一般伴随IO错误. -- dbv工具检查发现. 错误的本质原因包括: --对象在当前操作开始后,被其他用户删除. --数据块头记录的块类型不正确.比如 数据块的类型为6(Type=6),但块不是数据块。这种情况一般发生突然断电,存储故障等情况下。也是我们目前遇到的最多的情况。也就是说ora-8103,本质上可能是一个块损坏,而不是整个段都出现问题,最容易发生在操作系统或硬件故障的情况下,特别容易发生在扩展 extent、或者是格式化新块的时候。 --存储数据字典中的data_object_id与块中存储的data_object_id不一致。一些操作可能改变对象的data_object_id,比如删除表、trace table、重建索引,move表等。 --因为一些原因,可能引发一个extent被两个段使用的情况,可以使用如下脚本进行检查: 本地管理表空间: oradebug setmypid execute dbms_space_admin.tablespace_verify('&tablespace_name') oradebug tracefile_name assm表空间检查脚本: oradebug setmypid execute dbms_space_admin.assm_tablespace_verify ('&tablespace_name',dbms_space_admin.TS_VERIFY_BITMAPS) oradebug tracefile_name
如果存在不一致,dbms_space_admin将会生成一个trace文件。
由于字典管理表空间(DMT)基本不使用了,在此不做介绍。可以参考Metalink文档:Note 136697.1。 2.找到错误的对象 如何找到错误的对象呢?如果是SQL语句引发的错误,错误的对象要么是表,要么是索引。对于表,我们可以运行analyze来验证:analyze table <table_name> validate structure;
当然也可以使用一个全表扫描的操作,可以使用"FULL"提示字来强制查询语句走全表扫描。对索引也同样可以使用analyze命令:
analyze index <index_name> validate structure; 如果是因为对象类型错误引发的8103错误,可以使用event来跟踪这个错误: alter session set events '8103 trace name errorstack level 1'; 如果是通过dbv工具进行检测到的错误,dbv工具会直接报告错误的block id,通过block_id可查询到具体的对象。3. 解决错误
如果是数据块错误,可以尝试下列的步骤: a. 清空数据库高速缓存,看看这个错误是否是由于内存问题引起的。10g可以使用下列语法:alter system flush buffer_cache;
9i及以前的版本,需要使用event:
alter session set events 'immediate trace name flush_cache level 1';
在RAC系统中,需要在RAC中的每个节点都进行清空高速缓存的操作。清空高速缓存,会严重影响系统性能,因为所有的数据块需要重新载入到内存中,这个操作是非常昂贵的。一般建议错开高峰执行。b.如果损坏的对象是索引,就比较好办了,直接删除重建这个索引即可。
c.如果损坏泊是表,就比较麻烦了。如果数据是一些没有变化的字典表(比如,部门表、人员表这些),可以先truncate表,再使用imp工具从备份中导入。或者使用物理备份做介质恢复。RMAN的块恢复功能也可用于修复部分8103错误。 如果从备份恢复不是可行的(没有备份,或是恢复代价太大),可以"跳过"引发8103错误的的数据块,oracle提供了一个参考的脚本: --创建一个用来保存错误rowid的表 create table bad_rows (row_id rowid); set serveroutput on declare nrows number; badrows number; begin badrows:=0; nrows:=0; --使用索引构建一个循环,因为索引保存了表中所有行的rowid,注意这里使用了index提示字来强制索引 for i in (select /*+ index (tab1) */ rowid, <indexed column name> from <original table name> tab1) loop begin --将"好的"数据,insert into到新表中,通过rowid读取每一行 insert into <new table name> select <list of columns from table (ie col1, col2,..)> from <orig table name> where rowid=i.rowid; if (mod(nrows,10000)=0) then commit; end if; --如果发生错误,则insert错误的rowid到临时表中 exception when others then badrows:=badrows+1; insert into bad_rows values (i.rowid); commit; end; nrows:=nrows+1; end loop; dbms_output.put_line('Total rows: '||to_char(nrows)||' Bad rows: '||to_char(badrows)); end; / 脚本中的关键是使用索引来获取行中每一行的rowid,通过索引中保存的rowid,来取得完整的行。 还有一个方法就是使用dbms_repair表来跳过损坏的数据块,方法与ORA-01578,的处理方法相同。