Oracle RAC 数据库因 ORA-08103 逻辑损坏引发 Trace 文件爆炸故障处理


故障概述

故障现象

  • 告警内容:数据库服务器节点本地文件系统使用率飙升至 90.09%
  • 根本原因/u01/app/oracle/diag/rdbms/<DB_NAME>/<INSTANCE>/trace 目录下短时间内生成海量 trace 文件,迅速耗尽磁盘空间。

影响范围

  • 单节点 I/O 压力剧增,可能导致:
    • 数据库响应变慢
    • 新会话无法建立
    • 严重时实例崩溃(因无法写入 alert log)
  • 若未及时处理,可能波及整个 RAC 集群的稳定性。

核心错误

  • ORA-08103: object no longer exists
    此错误并非字面意义“对象被删除”,而是指 数据块的逻辑归属与当前数据字典不一致,属于典型的逻辑损坏(Logical Corruption)

故障根因分析

直接诱因:大规模 MOVE 表操作

  • 操作对象TBAAA.CS_REC_BUSIOBJ 表(一张按日期分区的大流水表)
  • 操作内容:因表碎片化严重,对多个分区执行了 ALTER TABLE ... MOVE PARTITION 操作。

💡 MOVE 操作的本质
Oracle 会为表/分区分配全新的物理存储段(Segment),并赋予其新的 DATA_OBJECT_ID。旧段被标记为可回收,但其数据块在磁盘上不会立即被物理擦除。

举例说明:

-- 创建一张表
CREATE TABLE test_obj AS SELECT * FROM dual;

-- 查询其ID
SELECT object_id, data_object_id FROM user_objects WHERE object_name = 'TEST_OBJ';
-- 假设输出: OBJECT_ID=90000, DATA_OBJECT_ID=90000

-- 执行 MOVE 操作
ALTER TABLE test_obj MOVE;

-- 再次查询
SELECT object_id, data_object_id FROM user_objects WHERE object_name = 'TEST_OBJ';
-- 输出: OBJECT_ID=90000 (不变), DATA_OBJECT_ID=90001 (变了!)

技术根源:Oracle Bug 28844866

  • Bug 标题High Waits For Event ‘GC Buffer Busy Acquire’ Due To ORA-8103 / ORA-1410
  • 影响版本:Oracle 12c, 18c, 19c(本例为 12.2.0.1.0
  • Bug 行为
    • 在 RAC 环境下,当发生 ORA-08103 时,会异常触发大量诊断信息写入。
    • 导致 trace 文件呈指数级增长,远超正常错误日志量级。

错误:OBJD MISMATCH

当一个长时间运行的查询在 MOVE 操作前后跨越执行时,可能发生以下情况:

  1. 查询开始时,获取到表的 DATA_OBJECT_ID = X
  2. MOVE 完成后,表的 DATA_OBJECT_ID 变为 X+1
  3. 查询后续访问的数据块若来自旧段(DATA_OBJECT_ID = X),而内存上下文期望的是 X+1,则触发 OBJD MISMATCH,抛出 ORA-08103

日志证据分析(关键步骤)

从 Alert Log 获取线索

$ORACLE_BASE/diag/.../alert_<SID>.log 中发现高频 ORA-8103

ORA-08103: object no longer exists
...
ADVISORY: Please collect redo for investigation of ORA-8103. Use command:
ALTER SYSTEM DUMP REDO scn min 1 scn max 16716635042430 dba min 32 2100068 dba max 32 2100068;

📌 注意:此建议命令主要用于 Oracle Support 分析,一线 DBA 可忽略。

深入 Trace 文件定位问题对象

选取一个典型 trace 文件(如 ora_12345.trc),关键内容如下:

*** SESSION ID:(1994.64263) 2020-05-20T11:47:30.893213+08:00
OBJD MISMATCH typ=6, seg.obj=286445, diskobj=286437, dsflg=100000, dsobj=286445, tid=286445, cls=1
kcbz_exec_ckf: check function 0x10b053f0 returned error=8103:1

BH (0xb9e386738) file#: 29 rdba: 0x07600b64 (29/2100068) class: 1 ba: 0xb93f1a000
  obj: 286445 objn: 286445 tsn: [0/18] afn: 29

buffer tsn: 18 rdba: 0x07600b64 (29/2100068)
Block header dump: 0x07600b64
  Object id on Block? Y
  seg/obj: 0x45ee5 csc: 0x00000ef425897404 itc: 40 flg: E typ: 1 - DATA

关键字段解读:

字段 含义
seg.obj / objn 286445 内存中该段的 DATA_OBJECT_ID
diskobj / seg/obj 0x45ee5 = 286437 磁盘块头中记录的 DATA_OBJECT_ID
file# / afn 29 绝对文件号
rdba 29/2100068 数据块物理地址(文件29,块2100068)

结论:内存认为块属于 286445,但磁盘块自称属于 286437OBJD 不匹配

反查对象名称

使用 DATA_OBJECT_ID = 286445 查询字典视图:

SELECT owner, object_name, subobject_name, object_type
FROM dba_objects
WHERE data_object_id = 286445;

返回结果

OWNER OBJECT_NAME SUBOBJECT_NAME OBJECT_TYPE
TBCS CS_REC_BUSIOBJ P_202005 TABLE PARTITION

🔍 定位完成:问题精确到 TBCS.CS_REC_BUSIOBJ 表的 P_202005 分区


故障处理过程

尝试方案一:刷新内存缓存(失败)

-- 清空 Buffer Cache,强制后续读取磁盘新块
ALTER SYSTEM FLUSH BUFFER_CACHE;

-- 清空 Shared Pool,清除可能失效的游标
ALTER SYSTEM FLUSH SHARED_POOL;

分析:

  • FLUSH BUFFER_CACHE:清空 SGA 中的 Buffer Cache。目的是清除所有可能存在的、带有旧 DATA_OBJECT_ID 的脏块或干净块。理论上,下次再读这个块时,会从磁盘读取最新的、正确的块。
  • FLUSH SHARED_POOL:清空 Shared Pool,其中包括 SQL 的执行计划(游标)。如果错误是由一个失效的、基于旧对象结构的游标引起的,刷新 Shared Pool 可以强制 SQL 重新解析,从而获取新的对象信息。

为什么失败?

  • 因为问题的根源不在内存,而在磁盘MOVE 操作虽然创建了新段,但旧段的数据块在磁盘上并不会立即被物理擦除。它们只是被“遗弃”了。然而,由于 Bug 或极端的并发情况,Oracle 的某些内部机制可能仍然会尝试去访问这些“幽灵”块。刷新内存只能保证以后读的是新块,但如果 Oracle 的代码逻辑因为 Bug 而错误地指向了旧块地址,那么从磁盘读上来的依然是错误的块,问题依旧。

尝试方案二:验证物理坏块(不适用)

使用 RMAN VALIDATEdbv 检查数据文件:

示例命令

# 使用 dbv 检查特定文件
$ dbv file=/path/to/datafile.dbf blocksize=8192

# 使用 RMAN 检查
RMAN> VALIDATE DATAFILE 29;

为什么这里不适用?

  • ORA-8103逻辑损坏,不是物理损坏。数据文件本身是完好的,I/O 没有问题,校验和也是正确的。问题在于块内部存储的元数据(seg/obj)与当前字典视图中的信息不一致。dbvRMAN VALIDATE 主要检测物理层面的问题(如断裂的块、校验和失败),对这种逻辑不一致无能为力。

最终解决方案:在线重建问题分区(成功)

-- 1. 首先,确认分区及其 DATA_OBJECT_ID
SELECT partition_name, data_object_id 
FROM dba_tab_partitions 
WHERE table_owner = 'TBCS' AND table_name = 'CS_REC_BUSIOBJ';

-- 2. 执行在线 MOVE 分区
-- ONLINE 关键字是 12c 引入的强大特性,允许在 MOVE 期间,DML (INSERT/UPDATE/DELETE) 操作可以并发进行。
ALTER TABLE TBCS.CS_REC_BUSIOBJ 
MOVE PARTITION P_202005 
ONLINE
TABLESPACE your_tablespace_name -- 可选,可以移动到不同表空间
UPDATE INDEXES; -- 非常重要!自动维护本地和全局索引,避免索引失效

为什么这个操作能解决问题?

  1. 彻底重建MOVE PARTITION 会为这个分区创建一个全新的、干净的段。这个新段的所有数据块都会被打上当前、正确DATA_OBJECT_ID
  2. 抛弃旧段:旧的、包含“幽灵”块的段会被完全废弃。Oracle 的字典视图会立即指向新的段。
  3. 消除不一致:无论之前是因为什么原因(Bug 或并发)导致访问了错误的块,现在那个错误的块所在的旧段已经不存在了。所有未来的访问都只会命中新的、一致的块。
  4. ONLINE 的优势:在 12c 之前,MOVE 操作会锁表,导致业务中断。ONLINE 选项通过使用物化视图日志等技术,实现了几乎无阻塞的在线重组,极大地提升了可用性。
  5. UPDATE INDEXES:这是一个最佳实践。MOVE 操作会使索引变为 UNUSABLE 状态。UPDATE INDEXES 子句会自动在后台重建这些索引,省去了手动维护的麻烦。

注意事项(报告总结 3.3)

  • Library Cache Lock:在 MOVE 操作开始和结束的瞬间,Oracle 需要获取表的排他锁来更新数据字典。这会导致一个非常短暂的 library cache lock 等待事件。对于绝大多数系统来说,这个等待是毫秒级的,可以忽略。但在极高并发的 OLTP 系统中,需要留意。

⚠️ 注意:操作瞬间会产生短暂 library cache lock,但 ONLINE 保证 DML 可并发。


Trace 文件清理指南

是否需要删除?

场景 建议
故障正在发生,磁盘 >90% 紧急删除部分旧文件,保留最新 1~2 个用于分析
故障已解决,原因明确 可以安全删除
已提交 SR 给 Oracle Support 按工程师要求保留,勿擅自删除

安全清理方法(推荐 ADRCI)

# 1. 进入 ADRCI
$ adrci

# 2. 查看并设置 home
adrci> show homes
adrci> set home diag/rdbms/orcl/ORCL1

# 3. 清理 7 天前的 trace 文件(单位:分钟)
adrci> purge -age 10080 -type TRACE

# 4. (可选)设置 ADR 空间上限(10GB)
adrci> set control (ADR_SIZE_LIMIT = 10737418240)

应急手动清理(谨慎!)

cd $ORACLE_BASE/diag/rdbms/orcl/ORCL1/trace

# 删除1天前的 .trc 文件
find . -name "*.trc" -mtime +1 -delete

# 或仅删特定错误相关文件
find . -name "*ora_8103*.trc" -delete

禁止rm -rf *.trc —— 可能删除正在写入的 active 文件!


预防措施与最佳实践

根本性修复

  • 应用官方补丁
    通过 My Oracle Support (MOS) 搜索 Bug 28844866,下载并安装对应的 RU/PSU

运维规范

  • DDL 窗口管理
    MOVESHRINK 等高风险 DDL 应安排在业务低峰期,并提前通知应用团队。
  • 优先使用 Online DDL
    Oracle 12c+ 支持 MOVE ... ONLINE,极大降低业务影响。

监控与告警

  • 磁盘监控
    $ORACLE_BASE/diag 所在文件系统设置 >80% 使用率告警

  • 自动清理脚本(示例 crontab):

    # 每天凌晨2点清理7天前的trace
    0 2 * * * adrci exec="set home diag/rdbms/orcl/ORCL1; purge -age 10080 -type TRACE"
    

ADR 配置优化

-- 在 ADRCI 中设置保留策略
adrci> set control (SHORTP_POLICY = 720)   -- trace 保留 30 天
adrci> set control (LONGP_POLICY = 8760)   -- alert log 保留 365 天

关键概念科普

OBJECT_ID vs DATA_OBJECT_ID

属性 OBJECT_ID DATA_OBJECT_ID
是否变化 永久不变(除非 DROP) DDL(MOVE/TRUNCATE等)后改变
用途 SQL 解析、依赖关系 物理存储、块一致性检查
查询视图 DBA_OBJECTS.OBJECT_ID DBA_OBJECTS.DATA_OBJECT_ID

ORA-08103 的通用排查思路

  1. 定位对象:从 trace 文件中找到 objnseg/obj 的值,通过 DBA_OBJECTS 查出具体的表或索引。

    SELECT owner, object_name, object_type FROM dba_objects WHERE data_object_id = &objn;
    
  2. 回顾近期操作:检查是否在近期对该对象执行过 TRUNCATE, MOVE, SHRINK, SPLIT/MERGE PARTITION 等 DDL 操作。

  3. 尝试轻量级修复

    • 如果是偶发的、孤立的错误,可以尝试 FLUSH BUFFER_CACHE
    • 如果怀疑是游标问题,可以 FLUSH SHARED_POOL(但要评估对性能的影响)。
  4. 针对不同对象类型采取措施

    • 如果是索引:最简单的办法是 REBUILD 索引。

      ALTER INDEX your_index_name REBUILD ONLINE;
      
    • 如果是表或表分区MOVE 表或分区是最直接有效的方法。

  5. 终极手段:如果以上都不行,或者损坏范围很大,可能需要考虑从备份中恢复(RMAN)或使用 EXPDP/IMPDP 逻辑导出导入来重建对象。