表扫描与索引扫描返回的行数不一致
某个应用最近总出现死锁,其中一些是因为报了索引和数据行存在不匹配的问题,MOS中有如下文档可以参考。
ORA-1499. Table/Index row count mismatch(文档 ID 563070.1)
现象:
使用“validate structure cascade”分析表时报ORA-1499的错误,trace文件中包含“Table/Index row count mismatch”的错误信息。例如:
SQL> analyze table test validate structure cascade; analyze table test validate structure cascade * ERROR at line 1: ORA-01499: table/index cross reference failure - see trace file
trace文件中包含:
Table/Index row count mismatch table 6559 : index 10000, 0 Index root = tsn: 6 rdba: 0x01400091
意味着扫描表返回6559行数据,索引扫描返回10000行数据。“Index root”是索引的段头信息。rdba: 0x01400091是相对于数据块地址的索引段头。他是十进制的20971665,Rfile#=5,Block#=145。
SQL> select dbms_utility.data_block_address_file(20971665) "Rfile#" ,dbms_utility.data_block_address_block(20971665) "Block#" from dual; Rfile# Block# ---------- ---------- 5 145
运行下面的查询明确关联的索引:
SQL> select owner, segment_name, segment_type from dba_segments where header_file = 5 and header_block = 145; OWNER SEGMENT_NAME SEGMENT_TYPE -------- --------------- ------------------ SCOTT I_TEST INDEX
这种逻辑不一致性也能通过10g以上版本的ORA-600 [kdsgrp1]错误或低版本的ORA-600 [12700]错误来说明。
原因:
这是一种表与索引之间的逻辑不一致。这种逻辑不一致通常是因为表上的高水位(HWM)出现了问题,全表扫描比索引扫描返回了更少的行。这种不一致性也可能是由于
Oracle的defect或会引起IO丢失的OS/硬件问题导致的。
解决方案:
可以通过下面的语句查询出全表扫描时未扫出的索引行:
select /*+ INDEX_FFS() */ rowid , dbms_rowid.ROWID_RELATIVE_FNO(rowid) relative_fno , dbms_rowid.ROWID_BLOCK_NUMBER(rowid) block from where is not null minus select /*+ FULL( )*/ rowid , dbms_rowid.ROWID_RELATIVE_FNO(rowid) relative_fno , dbms_rowid.ROWID_BLOCK_NUMBER(rowid) block from ; 实例: select /*+ INDEX_FFS(TEST I_TEST) */ rowid , dbms_rowid.ROWID_RELATIVE_FNO(rowid) relative_fno , dbms_rowid.ROWID_BLOCK_NUMBER(rowid) block from test where c2 is not null minus select /*+ FULL(TEST)*/ rowid , dbms_rowid.ROWID_RELATIVE_FNO(rowid) relative_fno , dbms_rowid.ROWID_BLOCK_NUMBER(rowid) block from test;
使用下面PLSQ中的索引,可以将全表扫描丢失的行存储到另一张表中:
drop table test_copy;
create table test_copy as select * from test where 1=2;
declare
cursor missing_rows is
select /*+ INDEX_FFS(TEST I_TEST) */ rowid rid
from test
where c2 is not null
minus
select /*+ FULL(TEST)*/ rowid rid
from test;
begin
for i in missing_rows loop
insert into TEST_COPY
select /*+ ROWID(TEST) */ * from TEST where rowid = i.rid;
end loop;
end;
/
- 当索引返回的比表记录少时,重建索引可以解决这个问题。
- 当索引返回的比表记录多时,重建索引或执行虚拟insert插入该表的操作以提高HWM,可以最终解决这种逻辑错误。在以上这个案例中,修复了逻辑错误,但这些行也还是可能丢失了,因为是在执行这里提到的方法之前运行了上述PLSQL脚本。
如果从Oracle Support需要额外的帮助,请提供:
1. analyze语句分析的trace文件。
2. 第一个查询语句的结果。
3. dump基表段头产生的trace文件。
select header_file, header_block, tablespace_name from dba_segments
where owner = upper('&table_owner') and segment_name = upper('&table_name');
alter system dump datafile &header_file block &header_block;
trace文件用来明确HWM。
@ Example from a block dump not using ASSM (Automatic Segment Space Management):
@
@ Extent Control Header
@ -----------------------------------------------------------------
@ Extent Header:: spare1: 0 spare2: 0 #extents: 4 #blocks: 31
@ last map 0x00000000 #maps: 0 offset: 4128
@ Highwater:: 0x01400