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,的处理方法相同。