Oracle 索引组织表 ORA-00600 [6200] 处理实战:从十六进制对象号定位到 IOT 重建
大家好,我是睿。
最近处理了一次比较典型、但又容易让人紧张的 Oracle 故障:数据库 open 后,后台进程不断在 alert 日志和 trace 中报 ORA-00600 [6200] ,同时伴随 ORA-07445 [kghstack_err()+117] 。
ORA-00600 这种错误,很多 DBA 第一眼看到都会头皮一紧。因为它不是普通 SQL 报错,而是 Oracle 内部错误。它可能是 bug,也可能是数据块、索引块、字典对象或者内存访问触发的问题。
这次的关键线索不在 alert 日志表面,而是在 trace 文件里:
**** key (begin=0x14b4, len=983) overlaps with another
begin = 0x14de len = 555
---- end index block validation
Block header dump: 0x00801e55
Object id on Block? Y
seg/obj: 0x302a csc: 0x00.f7883927 itc: 2 flg: E typ: 2 - INDEX
其中最关键的是两点:
1. key overlaps with another :说明索引块内部 key 之间发生重叠,基本可以往逻辑坏块、索引结构异常方向判断。
2. seg/obj: 0x302a :这是定位问题对象的关键。它是十六进制,需要转换成十进制后再去数据库字典里反查。
今天这篇文章,就把这次从 trace 定位对象、十六进制转换、识别 IOT、处理 ORA-28650 、最后重建对象的完整过程整理出来。
现场库名、路径、客户信息都已经脱敏, Oracle 自身对象名保留,因为这是排查逻辑的一部分。
数据库 open 后,alert 日志中出现类似报错:
Errors in file /u01/oracle/diag/rdbms/<db_unique_name>/<instance_name>/trace/<instance_name>_mmon_<pid>.trc
ORA-00600: internal error code, arguments: [6200], [18446744073709551615], [], [], [], [], [], [], [], [], [], []
Incident details in:
/u01/oracle/diag/rdbms/<db_unique_name>/<instance_name>/incident/incdir_<incident_id>/<instance_name>_mmon_<pid>_i<incident_id>.trc
Use ADRCI or Support Workbench to package the incident.
See Note 411.1 at My Oracle Support for error and packaging details.
Exception [type: SIGSEGV, SI_KERNEL(general_protection)]
[ADDR:0x0] [PC:0x2C3793B, kghstack_err()+117]
随后又出现:
ORA-07445: exception encountered: core dump [kghstack_err()+117] [SIGSEGV]
ORA-00600: internal error code, arguments: [6200], [18446744073709551615], [], [], [], [], [], [], [], [], [], []
这里有几个判断点:
3. 报错进程是 MMON ,不是普通业务会话。
4. ORA-00600 [6200] 是内部错误,需要继续看 incident trace。
5. 第二个参数 18446744073709551615 是 2^64 - 1 ,按无符号 64 位整数看是最大值,很多内部场景里可以理解成一个类似 -1 的特殊标记。排查时不要只盯着这个参数,真正有价值的是 trace 里后面的块校验信息。
这类问题我一般不会直接猜,而是先把 trace 文件打开,把能定位对象的线索找出来。
incident trace 里有这样一段:
**** key (begin=0x14b4, len=983) overlaps with another
begin = 0x14de len = 555
---- end index block validation
Block header dump: 0x00801e55
Object id on Block? Y
seg/obj: 0x302a csc: 0x00.f7883927 itc: 2 flg: E typ: 2 - INDEX
brn: 0 bdba: 0x801e50 ver: 0x01 opc: 0
先拆一下这段信息:
6. key overlaps with another :索引块内部 key 重叠,这是非常强的索引块逻辑异常信号。
7. typ: 2 - INDEX :块类型是索引块。
8. seg/obj: 0x302a :这个块属于哪个段对象,要靠它定位。
9. Block header dump: 0x00801e55 :这是块地址,也可以进一步拆出 file# 和 block#。
所以排查主线很清晰:
trace 发现索引块异常
-> 取 seg/obj 十六进制对象号
-> 转成十进制
-> 到 DBA_OBJECTS 反查对象
-> 判断对象类型和所属表
-> 决定修复方式
trace 里的 seg/obj: 0x302a 不能直接拿去查 DBA_OBJECTS 。数据库字典里的 OBJECT_ID 、 DATA_OBJECT_ID 是十进制,所以第一步要把 0x302a 转成十进制。
十六进制的 0x302a ,去掉前缀 0x 后是:
302A
十六进制每一位的权重是 16 的幂:
302A
= 3 * 16^3 + 0 * 16^2 + 2 * 16^1 + A * 16^0
= 3 * 4096 + 0 * 256 + 2 * 16 + 10 * 1
= 12288 + 0 + 32 + 10
= 12330
所以:
0x302a = 12330
在数据库里可以直接用 TO_NUMBER 转:
select to_number('302A', 'XXXX') as data_object_id
from dual;
输出:
DATA_OBJECT_ID
--------------
12330
注意, TO_NUMBER 里不要带 0x 前缀,直接传 302A 即可。
如果想写得更通用一点:
select to_number(replace(upper('0x302a'), '0X', ''), 'XXXX') as data_object_id
from dual;
如果当时不方便进数据库,也可以在 Linux 上直接转:
printf "%d\n" 0x302a
或者用 bc :
echo "ibase=16;302A" | bc
输出都是:
12330
这个小技巧很实用。遇到 trace 里的 seg/obj 、 rdba 、 bdba 这类十六进制值时,不要凭感觉猜,先转成十进制再查字典。
有了十进制对象号 12330 ,开始查字典。
这里我建议不要只查 OBJECT_ID ,也把 DATA_OBJECT_ID 一起查。因为块头里的 seg/obj 更接近段对象 ID,在对象经历过 truncate、move、重建后, OBJECT_ID 和 DATA_OBJECT_ID 可能不完全一样。
select owner,
object_name,
object_type,
object_id,
data_object_id
from dba_objects
where object_id = 12330
or data_object_id = 12330;
本次查到:
OWNER OBJECT_NAME OBJECT_TYPE OBJECT_ID DATA_OBJECT_ID
----- ----------------- ----------- --------- --------------
SYS SYS_IOT_TOP_12329 INDEX 12330 12330
对象名是 SYS_IOT_TOP_12329 ,对象类型是 INDEX 。
看到 SYS_IOT_TOP_ 这个前缀,基本就要想到:这不是普通索引,大概率是 IOT(Index Organized Table,索引组织表)的主键索引。
继续查它属于哪张表:
select owner,
index_name,
table_owner,
table_name,
index_type
from dba_indexes
where owner = 'SYS'
and index_name = 'SYS_IOT_TOP_12329';
输出:
OWNER INDEX_NAME TABLE_OWNER TABLE_NAME INDEX_TYPE
----- ----------------- ----------- -------------- ----------
SYS SYS_IOT_TOP_12329 SYS AQ$_ALERT_QT_T IOT - TOP
最终定位到问题对象:
SYS.AQ$_ALERT_QT_T
这是一个 SYS 下的 AQ 相关内部对象,并且底层是 IOT。
trace 里还有一个块地址:
Block header dump: 0x00801e55
这个值也很有用。它可以转成十进制 RDBA,再拆出相对文件号和块号。
先转十进制:
select to_number('00801E55', 'XXXXXXXX') as rdba_dec
from dual;
输出:
RDBA_DEC
--------
8396373
再用 DBMS_UTILITY 拆 file# 和 block#:
select dbms_utility.data_block_address_file(
to_number('00801E55', 'XXXXXXXX')
) as file_no,
dbms_utility.data_block_address_block(
to_number('00801E55', 'XXXXXXXX')
) as block_no
from dual;
输出示例:
FILE_NO BLOCK_NO
------- --------
2 7765
它背后的计算逻辑也不复杂。 Oracle RDBA 中低 22 位表示 block#,高位表示 file#:
2^22 = 4194304
file# = floor(8396373 / 4194304) = 2
block# = mod(8396373, 4194304) = 7765
如果想进一步确认这个块在哪个数据文件里:
select file#,
name
from v$datafile
where file# = 2;
如果需要对单块做进一步检查,也可以用 dbv :
dbv file=/path/to/datafile.dbf blocksize=8192 start=7765 end=7765
不过这次 trace 已经明确指向索引块逻辑异常,修复重点不在恢复单个数据块,而在处理对应的 IOT 对象。
既然定位到的是索引,第一反应通常是重建索引:
alter index SYS.SYS_IOT_TOP_12329 rebuild;
但执行后报错:
ORA-28650: Primary index on an IOT cannot be rebuilt
这个报错非常关键。
普通堆表的索引坏了,大多数情况下可以 rebuild 。但是 IOT 不一样。IOT 的数据本身就是按照主键存放在 B-tree 结构里的,所谓的主键索引就是表本身的存储结构。
也就是说:
普通表:表段 + 索引段,索引坏了可以单独 rebuild
IOT :主键索引就是表本体,不能像普通索引一样单独 rebuild
所以 SYS_IOT_TOP_12329 不能直接 rebuild。要修,就要围绕 IOT 对象本身做处理。
处理 SYS 下的内部对象,第一原则是:先备份,再动手。
本次先把数据备份出来:
create table SYS.AQ$_ALERT_QT_T_BAK
as
select *
from SYS.AQ$_ALERT_QT_T;
建议同时做几项确认:
select count(*) from SYS.AQ$_ALERT_QT_T;
select count(*) from SYS.AQ$_ALERT_QT_T_BAK;
select owner,
object_name,
object_type,
status
from dba_objects
where owner = 'SYS'
and object_name in ('AQ$_ALERT_QT_T', 'AQ$_ALERT_QT_T_BAK');
如果是生产库,我还会建议在操作前保留:
10. 当前 alert 日志片段。
11. incident trace 文件。
12. DBMS_METADATA.GET_DDL 输出。
13. 备份表行数。
14. 数据库备份或快照恢复手段。
这种 SYS 内部对象处理,不要只留一段命令。后面复盘或者回退时,证据链很重要。
接着抽取原对象 DDL:
select dbms_metadata.get_ddl('TABLE', 'AQ$_ALERT_QT_T', 'SYS')
from dual;
脱敏整理后的核心结构如下:
CREATE TABLE "SYS"."AQ$_ALERT_QT_T"
(
"NEXT_DATE" TIMESTAMP (6),
"TXN_ID" VARCHAR2(30),
"MSGID" RAW(16),
"ACTION" NUMBER,
PRIMARY KEY ("NEXT_DATE", "TXN_ID", "MSGID") ENABLE
)
USAGE QUEUE ORGANIZATION INDEX
NOCOMPRESS
PCTFREE 10
INITRANS 2
MAXTRANS 255
LOGGING
TABLESPACE "SYSAUX"
PCTTHRESHOLD 50;
这里再次确认:它不是普通 heap table,而是:
USAGE QUEUE ORGANIZATION INDEX
也就是 AQ 相关的 IOT 结构。
尝试直接删除:
drop table SYS.AQ$_ALERT_QT_T;
报错:
ORA-24005: Inappropriate utilities used to perform DDL on AQ table SYS.AQ$_ALERT_QT_T
这个错误也符合预期。因为它是 AQ 相关对象,不允许像普通表一样直接用 DDL 工具处理。
标准思路上, AQ queue table 应该优先考虑 DBMS_AQADM 这类包进行管理。但这次是 SYS 内部 AQ 对象异常,并且对象本身已经触发内部错误,现场最终采用了维护窗口下的应急处理方式。
执行前先开启事件:
alter session set events '10851 trace name context forever, level 1';
然后删除对象:
drop table SYS.AQ$_ALERT_QT_T;
删除后关闭事件:
alter session set events '10851 trace name context off';
这里必须强调一句: 10851 这类事件属于非常规处理手段,不适合作为日常操作模板。生产环境动 SYS 内部对象前,建议至少满足几个条件:
15. 有完整备份或快照。
16. 有明确的问题对象定位证据。
17. 操作窗口可控。
18. 已评估对象用途和影响范围。
19. 必要时通过 Oracle Support 确认。
这一步不是 “胆子大就行”,而是要有回退和证据。
删除后,按 GET_DDL 抽出的结构尝试重建:
CREATE TABLE "SYS"."AQ$_ALERT_QT_T"
(
"NEXT_DATE" TIMESTAMP (6),
"TXN_ID" VARCHAR2(30),
"MSGID" RAW(16),
"ACTION" NUMBER,
PRIMARY KEY ("NEXT_DATE", "TXN_ID", "MSGID") ENABLE
)
USAGE QUEUE ORGANIZATION INDEX
NOCOMPRESS
PCTFREE 10
INITRANS 2
MAXTRANS 255
LOGGING
TABLESPACE "SYSAUX"
PCTTHRESHOLD 50;
表创建成功。
但是回灌数据时报错:
insert into SYS.AQ$_ALERT_QT_T
select *
from SYS.AQ$_ALERT_QT_T_BAK;
报错:
ORA-12899: value too large for column "SYS"."AQ$_ALERT_QT_T"."ACTION" (actual: 32, maximum: 22)
这里很容易让人疑惑: DDL 里 ACTION 是 NUMBER ,为什么会出现 actual: 32, maximum: 22 ?
我的理解是,这类 AQ 内部对象并不是普通业务表,内部列定义、隐藏属性、队列组织方式可能比 GET_DDL 展示出来的内容更复杂。单纯照 DDL 手工重建,未必能完整还原原对象的内部属性。
所以这一步的结论是:手工按 DDL 重建失败,不要硬插、不要改数据,改用更保守的 CTAS 方式保持列定义和数据一致。
重新开启事件后,先删除刚才创建的对象:
alter session set events '10851 trace name context forever, level 1';
drop table SYS.AQ$_ALERT_QT_T;
这次不再手写列定义,而是直接通过备份表 CTAS 回来:
create table SYS.AQ$_ALERT_QT_T
tablespace SYSAUX
as
select *
from SYS.AQ$_ALERT_QT_T_BAK;
然后补回主键:
alter table SYS.AQ$_ALERT_QT_T
add constraint PK_AQ_ALERT_QT_T
primary key ("NEXT_DATE", "TXN_ID", "MSGID");
最后关闭事件:
alter session set events '10851 trace name context off';
再检查索引:
select table_owner,
table_name,
owner,
index_name,
status
from dba_indexes
where table_owner = 'SYS'
and table_name = 'AQ$_ALERT_QT_T';
输出类似:
TABLE_NAME INDEX_NAME STATUS
-------------- ----------------- ------
AQ$_ALERT_QT_T PK_AQ_ALERT_QT_T VALID
这时原来那个异常的 SYS_IOT_TOP_12329 已经不再作为坏块对象存在,新的对象结构也能正常查询。
处理 SYS 对象,不能只看命令执行成功。后面验证一定要做完整。
select owner,
object_name,
object_type,
status
from dba_objects
where owner = 'SYS'
and object_name like 'AQ$_ALERT_QT_T%';
select owner,
index_name,
table_owner,
table_name,
status
from dba_indexes
where table_owner = 'SYS'
and table_name = 'AQ$_ALERT_QT_T';
select *
from v$database_block_corruption;
如果之前已经定位到 file# 和 block#,也可以用 dbv 对对应数据文件或对应块范围做补充检查。
tail -f /u01/oracle/diag/rdbms/<db_unique_name>/<instance_name>/trace/alert_<instance_name>.log
重点观察:
20. MMON 是否继续报 ORA-00600 [6200] 。
21. 是否还有新的 incident 生成。
22. 是否有新的 ORA-07445 。
23. 数据库 open 后后台进程是否稳定。
如果处理后不再出现新的 incident,相关对象状态正常,业务验证也没问题,基本可以判断本次处理完成。
这次处理过程里,我觉得最值得记住的是下面几个点。
第一, ORA-00600 不要只看 alert 日志,要看 incident trace。真正的定位信息往往在 trace 里的 block dump、stack、object id、rdba 这些细节里。
第二, seg/obj 是定位对象的关键,但它通常是十六进制。比如这次:
seg/obj: 0x302a
0x302a = 12330
转换后再查:
select owner, object_name, object_type
from dba_objects
where object_id = 12330
or data_object_id = 12330;
第三, Block header dump: 0x00801e55 这类 RDBA 也很重要。它可以拆出 file# 和 block#,帮助我们确认问题块落在哪个数据文件、哪个块上。
第四, IOT 的主键索引不能按普通索引处理。遇到:
ORA-28650: Primary index on an IOT cannot be rebuilt
说明你碰到的不是普通索引 rebuild 问题,而是 IOT 结构本身的问题。
第五, SYS 下 AQ 内部对象要谨慎处理。 ORA-24005 提醒我们:这不是普通表,不应该随便用普通 DDL 操作。现场使用事件绕过属于应急手段,不建议脱离备份和验证直接套用。
这次故障从表面看是:
数据库 open 后 MMON 报 ORA-00600 [6200]
但真正的处理路径是:
查看 incident trace
-> 发现 index block validation 异常
-> 通过 seg/obj: 0x302a 定位对象
-> 十六进制转十进制得到 12330
-> 反查到 SYS_IOT_TOP_12329
-> 确认属于 SYS.AQ$_ALERT_QT_T
-> 识别为 AQ 相关 IOT 对象
-> 普通 rebuild 报 ORA-28650
-> 备份数据
-> 维护窗口下删除并重建对象
-> 回灌数据、补主键、验证对象状态和 alert 日志
我一直觉得, DBA 处理故障最重要的不是“命令背得多”,而是能把线索串起来。
ORA-00600 看起来吓人,但只要 trace 里能定位对象、能解释块地址、能判断对象类型,就不是完全无从下手。
这篇文章里补的十六进制转换、 RDBA 拆解、IOT 主键索引不能 rebuild 这些点,都是生产排障里非常实用的小技能。平时不一定天天用,但真遇到内部错误时,往往就是这些细节帮你把问题从“玄学”拉回到“可定位、可验证、可处理”。
好了,今天这篇就到这里。你在生产环境里遇到过 ORA-00600 或索引逻辑坏块吗?是 rebuild 解决的,还是最后走了对象重建、RMAN、Support patch?欢迎在评论区聊聊你的处理经历。
—— 睿 | Oracle性能优化老司机
专注硬核干货,欢迎一起卷技术!

来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70047128/viewspace-3179403/ ,如需转载,请注明出处,否则将追究法律责任。