【Oracle专栏】明明有索引,但报ORA-08102未找到索引

Oracle && OceanBase 相关文档,希望互相学习,共同进步

风123456789~-CSDN博客


1.背景

Oracle 19c,今天准备清理一下之前跑的部分报表的数据,由于表中数据量比较大,大约680万数据,待清理 59万,于是分批按条件进行删除。但是执行一部分后,报错如下:

ORA-08102: 未找到索引关键字,对象号1955591,文件84,块1571386

本文主要针对大批量数据清理、索引异常排查 及问题解决,进行日志记录。

2. 实验

2.1 相关表及存储过程

需求:删除sun_public_info 表中,字段为 enum_name 的指定范围内的数据。同步删除关联的子表的数据。由于数据量比较大,编写包函数进行循环进行删除。

1)涉及表

需求:删除sun_public_info 表中,enum_name 在指定范围的数据。

2) 涉及处理过程

作用:将表中数据,循环删除

sql 复制代码
create or replace package PG_SUN_UPDATE  is
  
  /*--------------------------
     创建时间 : 2026.09.15
     创建人   : FENGLY
     说明     : TEST
  ---------------------------*/  
  function TEST2 (pDataIssue in varchar2) return varchar2;
 

end PG_SUN_UPDATE ;
/

CREATE OR REPLACE PACKAGE BODY PG_SUN_UPDATE  is

/*--------------------------
     创建时间 : 2026.09.15
     创建人   : FENGLY
     说明     : TEST
  ---------------------------*/  
  function TEST2 (pDataIssue in varchar2) return varchar2 is
     vResult       varchar2(10);
    vFunctionName varchar2(1000);
    vTableName    varchar2(1000);
    vEnumName     varchar2(100);
    vMsg          varchar2(1000);
    vCount1        number;
    vCount2        number;
    vSQL          varchar2(3000);
    vSUN_PUBLIC_DATA varchar2(100);

  begin
    vResult       := '0';
    vFunctionName := 'PG_SUN_UPDATE.TEST2  -上线前清空';
    vTableName    := 'sun_public_info -check ';

    BEGIN
      GET_LOGGER(pDataIssue, vFunctionName, vTableName, '0', '开始跑批');
      execute immediate 'alter table SUN_PUBLIC_INFO nologging';

      FOR vCursor IN ( SELECT T.KID,T.TABLE_NAME,T.ENUM_NAME,T.TITLE,T.KEY_CONTITION,
                                T.SUN_TABLE_NAME,
                                DECODE(T.TABLE_NAME ,'OPEN_FM_RPT_SR','OPEN_SUN_PUBLIC_DATA_SR'
                                                    ,'OPEN_FM_RPT_ZC','OPEN_SUN_PUBLIC_DATA_ZC'
                                      ,T.TABLE_NAME) OPEN_TABLE_NAME
                          FROM OUT_OPEN_PARAM_TABLES@out T
                        WHERE T.DELETED='1'  --新版自动跑批的17个表
                         AND T.ENUM_NAME NOT IN('ORG_STRUCTURE_PUBLIC','FOCUS_OPEN')
                         ORDER BY T.KID )
      LOOP
          vEnumName        := vCursor.ENUM_NAME ;
          vSUN_PUBLIC_DATA := vCursor.SUN_TABLE_NAME;

              --1)info表条数
          SELECT /*+PARALLEL(5)*/COUNT(*) into vCount1
           FROM SUN_PUBLIC_INFO T
          WHERE T.ENUM_NAME = vEnumName ;               
               
             --2)data表条数           
          vSQL := 'SELECT /*+PARALLEL(5)*/COUNT(*) FROM '||vSUN_PUBLIC_DATA;
          EXECUTE IMMEDIATE vSQL into vCount2;
         
          GET_LOGGER(pDataIssue, vFunctionName, vSUN_PUBLIC_DATA, '0', vEnumName||' info表:'||vCount1||' 条,data表:'||vCount2||' 条');

             --3)上线前清空
          execute immediate 'truncate table '||vSUN_PUBLIC_DATA; 
          IF vCount1 > 0 then
              DELETE /*+PARALLEL(5)*/
              FROM SUN_PUBLIC_INFO T
              WHERE T.ENUM_NAME = vEnumName ; 
              COMMIT;
          END IF;
          GET_LOGGER(pDataIssue, vFunctionName, vTableName||'-'||vSUN_PUBLIC_DATA, '0', vEnumName||' 清空结束');
      END LOOP;
      execute immediate 'alter table SUN_PUBLIC_INFO logging';
      GET_LOGGER(pDataIssue,  vFunctionName, vTableName, '0', '结束跑批');

    EXCEPTION
      WHEN OTHERS THEN
        vMsg := '报错位置:' || dbms_utility.format_error_backtrace || '报错信息:' ||
                substr(SQLERRM, 1, 200);
        GET_LOGGER(pDataIssue, vFunctionName, vEnumName, '3', vMsg);
        vResult := '1';
    END;
    return vResult;
  end;
   

end PG_SUN_UPDATE ;
/

2.2 报错浮现

报错位置:ORA-06512: 在 "xx.PG_SUN_UPDATE", line 496

ORA-06512: 在 "NH_MCRO_AFFAIRS.PG_SUN_UPDATE", line 496

报错信息:ORA-08102: 未找到索引关键字, 对象号 1955591, 文件 84, 块 1571386 (2)

看到表是数据量比较大,表做的日期分区,处理时用的 非分区字段的索引 enum_name,进行检索后删除,删除时报错,执行语句如下:

sql 复制代码
DELETE from SUN_PUBLIC_INFO T WHERE T.ENUM_NAME='FM_AS_CDQTZ'

把该语句从处理函数包中拿出,单独plsql执行,依然报错相同,问题浮现。

ORA-08102: 未找到索引关键字, 对象号 1955591, 文件 84, 块 1571386 (2)

一般是‌索引与表数据出现了逻辑不一致‌:索引中存储的键值条目和数据表中的实际数据不匹配,执行删除操作时Oracle尝试同步更新索引条目,却找不到对应的索引键,直接抛出异常终止操作。

  • 错误类型‌:Oracle数据库内部索引一致性异常

  • 核心指向 ‌:索引中存储的键值与对应数据表中的实际数据不匹配,导致数据库访问索引时无法找到对应关键字

  • 关联定位‌:通过对象号1955591可以在数据库中精准定位到发生异常的索引对象,文件84和块1571386标记了异常索引所在的物理存储位置
    常见触发原因:

  • 数据库运行过程中遭遇意外断电、硬件IO错误,导致索引数据块写入不完整

  • Oracle数据库自身的已知Bug引发索引与表数据不同步

  • 数据迁移、异常强制拉起数据库等非常规操作,破坏了索引的完整性

🛠️ 常规处理方案

  1. 定位异常对象 ‌:执行查询语句 select owner, object_name, object_type from dba_objects where object_id = 1955591;, 确认出问题的索引所属的用户、名称和类型。
  2. 在线重建索引 ‌:优先使用 alter index 索引名称 rebuild online; 命令重建索引,该方式会直接读取表的完整数据重新生成索引,避免沿用原有损坏的索引数据。
  3. 极端修复方式‌:如果重建无效,可以先删除该索引再重新创建

2.3 报错处理

1)定位对象

报错中,看到提示是索引未找到,查询对象号:

sql 复制代码
select owner, object_name, object_type 
from dba_objects 
where object_id = 1955591;

执行截图:发现确实是处理表的索引

联想到业务前端时间确实这个字段类型有变过,有可能索引失效,想着要么在线重建索引、或者删掉重建索引。

2)在线重建索引

sql 复制代码
alter index SUN_PUBLIC_INFO_AS_OF_EOM_DT_index rebuild online;

执行后,却提示 索引不存在。感觉挺奇怪的,查出来确实是本用户下的索引。于是,想着要么删了重建。

3)删掉重建索引

sql 复制代码
DROP INDEX SUN_PUBLIC_INFO_AS_OF_EOM_DT_index;

执行后,依然却提示 索引不存在。😅😅尴尬,还是不存在,明明dba_object 查到是它,却报不存在。小伙伴们,到这里你猜到什么原因了吗?

仔细看,

看到的索引列表里,SUN_PUBLIC_INFO_AS_OF_EOM_DT_index等索引明明在界面中显示存在,执行删除操作却提示不存在。 再仔细看,里面有个小写字母。而Oracle默认会把未加双引号的索引名自动转为大写,而这个索引时名称带小写后缀,直接写drop index SUN_PUBLIC_INFO_AS_OF_EOM_DT_index会被自动转成大写,自然找不到匹配的索引。

于是重新执行:

sql 复制代码
alter index "SUN_PUBLIC_INFO_AS_OF_EOM_DT_index" rebuild online;

或
DROP INDEX "SUN_PUBLIC_INFO_AS_OF_EOM_DT_index"

发现都执行正常了。原因就是创建索引时不规范,采用了部分字符小写导致的。

再重新执行之前报错的语句,OK

此时,问题解决。

2.4 检查清理结果

sql 复制代码
SELECT table_name, num_rows FROM user_tables
WHERE TABLE_NAME ='SUN_PUBLIC_INFO' 
ORDER BY table_name;
;

SELECT COUNT(*) FROM  SUN_PUBLIC_INFO;

结果截图:

发现,表中数据不一致。需要重新收集统计信息,才能让 num_rows 变为 0 或最新值。

sql 复制代码
--手动收集统计信息
BEGIN
  DBMS_STATS.GATHER_TABLE_STATS(
    ownname => 'xx',          -- 当前用户模式名
    tabname => 'SUN_PUBLIC_INFO',     -- 替换为具体的表名,如 'RPT_SALES'
    cascade => TRUE           -- 同时更新索引统计信息
  );
END;
/

再查,两表结果一致。

如果多个表,批量执行如下:

sql 复制代码
--使用匿名块批量处理符合条件的表
BEGIN
  FOR t IN (SELECT table_name FROM user_tables WHERE table_name LIKE '%RPT%') LOOP
    BEGIN
      DBMS_STATS.GATHER_TABLE_STATS(
        ownname => USER,
        tabname => t.table_name,
        cascade => TRUE
      );
    EXCEPTION
      WHEN OTHERS THEN
        DBMS_OUTPUT.PUT_LINE('Failed to gather stats for: ' || t.table_name);
    END;
  END LOOP;
END;
/

注意:

  1. 不要依赖 num_rows 判断实时行数 ‌:它仅用于优化器生成执行计划参考。如果需要知道确切行数,必须使用 SELECT COUNT(*)
  2. Truncate 后建议立即收集统计‌:尤其是对于大数据量的表,truncate 后如果不更新统计,优化器可能仍认为该表有大量数据,从而选择错误的执行计划(如不走索引或选择错误的连接方式)。
  3. 自动统计任务‌:Oracle 默认有自动统计收集任务(通常在夜间维护窗口运行),但如果你的业务对统计信息时效性要求高,建议在大批量数据变更(如 Truncate、大量 Insert)后手动触发收集。

3.总结

1)索引异常后,可以通过重建索引解决。注意索引名大小写。

2)select count(*) 和 num_rows 的区别

特性 SELECT COUNT(*) user_tables.num_rows
数据来源 实时扫描全表(或索引快速计数) 数据字典中的统计快照
准确性 绝对准确 可能过时‌(依赖上次收集时间)
性能 大表慢,消耗 I/O 极快‌,直接查字典
更新时机 每次查询都计算 仅在运行 DBMS_STATS 或自动任务时更新
DDL 影响 反映最新状态 不受 DDL(如 Truncate)自动影响

实验验证:ok


项目管理--相关知识

项目管理-项目绩效域1/2-CSDN博客

项目管理-项目绩效域1/2_八大绩效域和十大管理有什么联系-CSDN博客

项目管理-项目绩效域2/2_绩效域 团不策划-CSDN博客

高项-案例分析万能答案(作业分享)-CSDN博客

项目管理-计算题公式【复习】_项目管理进度计算题公式:乐观-CSDN博客

项目管理-配置管理与变更-CSDN博客

项目管理-项目管理科学基础-CSDN博客

项目管理-高级项目管理-CSDN博客

项目管理-相关知识(组织通用治理、组织通用管理、法律法规与标准规范)-CSDN博客


Oracle其他文档,希望互相学习,共同进步

Oracle-找回误删的表数据(LogMiner 挖掘日志)_oracle日志挖掘恢复数据-CSDN博客

oracle 跟踪文件--审计日志_oracle审计日志-CSDN博客

ORA-12899报错,遇到数据表某字段长度奇怪现象:"Oracle字符型,长度50"但length查却没有50_varchar(50) oracle 超出截断-CSDN博客

EXP-00091: Exporting questionable statistics.解决方案-CSDN博客

Oracle 更换监听端口-CSDN博客

相关推荐
Leon-Ning Liu21 小时前
Oracle 19c ASMB 进程 PGA 内存泄漏排查实战 (Bug 38610635)
数据库·oracle
步行cgn21 小时前
Spring 注入 null 和空字符串详解
数据库·spring·oracle
zone_z1 天前
11 · 空间类故障排查:表空间满 / 临时表空间爆 / 归档满挂库
oracle·temp·tablespace·archive·ora-01652
严同学正在努力2 天前
认识 SQL Server 的 T-SQL 语法
数据库·人工智能·ai·oracle·dba
l1t3 天前
DeepSeek总结的chdb-core v26.7.3发版说明
数据库·clickhouse·oracle
风哥2号3 天前
数据库教程FGMT21‑Oracle RAC+DG生产实战(1):Oracle19c/26ai for RHEL8/9安装配置+补丁
数据库·oracle
风哥2号3 天前
数据库教程FGMT26‑1‑GoldenGate数据库容灾迁移01(OGG同构异构、数据库迁移、数据同步、容灾复制)
数据库·oracle
风哥2号3 天前
数据库教程FGMT25‑Oracle多租户架构CDB与PDB运维管理
数据库·oracle
天空之城--4 天前
Flutter Drift 完全指南:从原理到实战
jvm·flutter·oracle