表扫描与索引扫描返回的行数不一致

某个应用最近总出现死锁,其中一些是因为报了索引和数据行存在不匹配的问题,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(<tablename> <indexname identified in 2.1>) */ rowid 
      , dbms_rowid.ROWID_RELATIVE_FNO(rowid) relative_fno 
      , dbms_rowid.ROWID_BLOCK_NUMBER(rowid) block 
from <tablename> 
where <indexed column> is not null 
minus 
select /*+ FULL(<tablename>)*/ rowid 
     , dbms_rowid.ROWID_RELATIVE_FNO(rowid) relative_fno 
     , dbms_rowid.ROWID_BLOCK_NUMBER(rowid) block 
from <tablename>; 

实例:

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:: 0x014000d6 ext#: 3 blk#: 5 ext size: 8  
@  
@ So, HWM is located at RDBA 0x014000d6 in extent_id=5 and block#=5 in that extent. 

4. 这个查询结果可以明确索引多返回的行的区id:

select rid, a.relative_fno, a.block, e.owner, e.segment_name, e.segment_type, e.extent_id, e.blocks 
from (select /*+ INDEX_FFS(<table name> <index name>) */ rowid rid 
            , dbms_rowid.ROWID_RELATIVE_FNO(rowid) relative_fno 
            , dbms_rowid.ROWID_BLOCK_NUMBER(rowid) block 
      from <table owner.table name> 
      where <indexed column> is not null 
      minus 
      select /*+ FULL(<table name>)*/ rowid rid 
            , dbms_rowid.ROWID_RELATIVE_FNO(rowid) relative_fno 
            , dbms_rowid.ROWID_BLOCK_NUMBER(rowid) block 
      from <table owner.table name> 
     ) a 
    , dba_extents e 
where a.relative_fno=e.relative_fno 
and e.tablespace_name = upper('&tablespace_name') 
and v.ts#=&tablespace_number 
and (a.block between e.block_id and e.block_id+blocks-1); 

注意:

- 以上SQL中请替换owner、表名、索引名和索引列。

- 表空间是上述步骤3提供的。

- 这个SQL查询提供了索引返回行位置的区extent。

时间: 2024-10-31 06:31:41

表扫描与索引扫描返回的行数不一致的相关文章

续《表扫描与索引扫描返回的行数不一致》

续<表扫描与索引扫描返回的行数不一致> 上篇文章主要介绍了如何从分析表得到的报错,以及trace中的信息,判断表返回的记录与索引返回记录不一致时的处理方式.下面这篇文章则介绍了针对ORA-1499进行问题排查的一些基本方法. OERR: ORA-1499 table/Index Cross Reference Failure - see trace file (文档 ID 1499.1) Error: ORA 1499 Text: table/Index Cross Reference Fai

mybatis mysql update 返回影响行数错误

问题描述 mybatis mysql update 返回影响行数错误 数据库有个订单表 有一行数据 order_id=1555(自增主键),有个标记字段order_mark=2 执行语句update t_order set order_mark = 2 where order_id=1555 使用mysql自带的client工具 Query OK 0 rows affected (0.00 sec)Rows matched: 1 Changed: 0 Warnings: 0 使用Navicat

如何返回指定行数之间的查询结果?

 如何返回指定行数之间的查询结果?     如何返回指定行数之间的查询结果,以实现web记录分页,在Oracle中有许多的方法,这里仅仅列出了4种,希望能对大家有所帮助,大家可以根据不同需要选择下面的script  1)select ... where rownum < 50 minus select ... where rownum < 30       这个方法因为用到了minus操作符,所以速度会受影响.   2)  SELECT results.* FROM   ( SELECT t2

mysql mybatis 执行update 返回影响行数错误

问题描述 mysql mybatis 执行update 返回影响行数错误 文章内容在:http://www.oschina.net/question/1244507_250862 我就不再次编辑了 解决方案 kohana返回db影响的行数,mysql影响的行数(delete,update)----------------------

文件换行:wc vi 与 awk 行数不一致

问题描述 文件换行:wc vi 与 awk 行数不一致 对同一文件: 用wc统计文件行数是两行: vi打开文件实际占了三行,后两行均是均只有^M,但文件统计显示也是只有两行: 若认为只是windows文件到linux下的显示问题,那为什么我用awk对文件操作,结果还是三行? 诚心求助.. 解决方案 最好是把windows文件转换为Linux格式 重要对应命令才能处理正确 解决方案二: http://blog.csdn.net/junmuzi/article/details/8608013 解决方

MySQL返回影响行数的测试示例

  found_rows() : select row_count() : update delete insert 注:需要配合相应的操作一起使用,否则返回的值只是1和-1(都是不正确的值) 示例: drop database if exists `mytest`; create database `mytest`; use `mytest`; drop table if exists `MyTestTable`; create table `MyTestTable`(`ID` int ,`N

修改注册表调整Win8开始屏幕磁贴显示行数

当我们进入Windows 8系统,首先印入眼帘的就是布满彩色磁贴的开始屏幕,细心的朋友也许会发现,不同设备上Win8开始屏幕的磁贴的行数显示是不同的.我们有办法调整Win8开始屏幕上的磁贴显示行数吗? 其实,在默认设置下,Win8系统会根据不同显示屏幕的分辨率来指定开始屏幕上的默认磁贴行数.下面右表中给出了不同分辨率屏幕中显示的默认.最大和最小的Win8磁贴行数.比如1024×768像素的屏幕,默认磁贴显示为四行.   图示:不同分辨率显示屏幕的Win8磁贴显示行数 如果我们想要自己调整Win8

第六章——根据执行计划优化性能(2)——查找表/索引扫描

原文:第六章--根据执行计划优化性能(2)--查找表/索引扫描 前言:       在绝大部分情况下,特别是从一个大表中返回少量数据时,表扫描或者索引扫描并不是一种高效的方式.这些必须找出来并解决它们从而提高性能,因为扫描将遍历每一行,查找符合条件的数据,然后返回结果.这种处理是相当耗时耗资源的.在性能优化过程中,一般集中于: 1.  CPU 2.  Network 3.  磁盘IO 而扫描操作会增加这三种资源的开销.   准备工作: 下面将创建两个表来查看不同的物理关联操作的不同影响.创建脚本

SQL SERVER中关于OR会导致索引扫描或全表扫描的浅析

在SQL SERVER的查询语句中使用OR是否会导致不走索引查找(Index Seek)或索引失效(堆表走全表扫描 (Table Scan).聚集索引表走聚集索引扫描(Clustered Index Scan))呢?是否所有情况都是如此?又该如何优化呢? 下面我们通过一些简单的例子来分析理解这些现象.下面的实验环境为SQL SERVER 2008,如果在不同版本有所区别,欢迎指正.   堆表单索引 首先我们构建我们测试需要实验环境,具体情况如下所示: DROP TABLE TEST    CRE