Oracle RAC 数据库因 ORA-08103 逻辑损坏引发 Trace 文件爆炸故障处理
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 文件呈指数级增长,远超正常错误日志量级。
- 在 RAC 环境下,当发生
错误:OBJD MISMATCH
当一个长时间运行的查询在 MOVE 操作前后跨越执行时,可能发生以下情况:
- 查询开始时,获取到表的
DATA_OBJECT_ID = X。 MOVE完成后,表的DATA_OBJECT_ID变为X+1。- 查询后续访问的数据块若来自旧段(
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,但磁盘块自称属于286437→ OBJD 不匹配。
反查对象名称
使用 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 VALIDATE 或 dbv 检查数据文件:
示例命令:
# 使用 dbv 检查特定文件
$ dbv file=/path/to/datafile.dbf blocksize=8192
# 使用 RMAN 检查
RMAN> VALIDATE DATAFILE 29;
为什么这里不适用?
ORA-8103是逻辑损坏,不是物理损坏。数据文件本身是完好的,I/O 没有问题,校验和也是正确的。问题在于块内部存储的元数据(seg/obj)与当前字典视图中的信息不一致。dbv和RMAN 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; -- 非常重要!自动维护本地和全局索引,避免索引失效
为什么这个操作能解决问题?
- 彻底重建:
MOVE PARTITION会为这个分区创建一个全新的、干净的段。这个新段的所有数据块都会被打上当前、正确的DATA_OBJECT_ID。 - 抛弃旧段:旧的、包含“幽灵”块的段会被完全废弃。Oracle 的字典视图会立即指向新的段。
- 消除不一致:无论之前是因为什么原因(Bug 或并发)导致访问了错误的块,现在那个错误的块所在的旧段已经不存在了。所有未来的访问都只会命中新的、一致的块。
ONLINE的优势:在 12c 之前,MOVE操作会锁表,导致业务中断。ONLINE选项通过使用物化视图日志等技术,实现了几乎无阻塞的在线重组,极大地提升了可用性。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 窗口管理:
MOVE、SHRINK等高风险 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 的通用排查思路
-
定位对象:从 trace 文件中找到
objn或seg/obj的值,通过DBA_OBJECTS查出具体的表或索引。SELECT owner, object_name, object_type FROM dba_objects WHERE data_object_id = &objn; -
回顾近期操作:检查是否在近期对该对象执行过
TRUNCATE,MOVE,SHRINK,SPLIT/MERGE PARTITION等 DDL 操作。 -
尝试轻量级修复:
- 如果是偶发的、孤立的错误,可以尝试
FLUSH BUFFER_CACHE。 - 如果怀疑是游标问题,可以
FLUSH SHARED_POOL(但要评估对性能的影响)。
- 如果是偶发的、孤立的错误,可以尝试
-
针对不同对象类型采取措施:
-
如果是索引:最简单的办法是
REBUILD索引。ALTER INDEX your_index_name REBUILD ONLINE; -
如果是表或表分区:
MOVE表或分区是最直接有效的方法。
-
-
终极手段:如果以上都不行,或者损坏范围很大,可能需要考虑从备份中恢复(
RMAN)或使用EXPDP/IMPDP逻辑导出导入来重建对象。
- 感谢你赐予我前进的力量

