故障描述
某客户发现其Oracle 11.2.0.4的数据库,alert日志里面报错ORA-600 4187。
故障分析
查看官方文档
Bug 19700135 - ORA-600 4187 when the undo segment wrap# is close to the max value of 0xffffffff
KI16267
是一个BUG。
什么是wrap#呢?
1. 我们先深入了解一下在回滚段
Oracle 的回滚段(Undo Segment)内部,有一个事务表(Transaction Table),通常位于回滚段的段头块(Segment Header Block)中。这个事务表由多个槽位(Slot)组成,每个槽位可以记录一个活动事务的状态。
2. 什么是Wrap#
Wrap#(Wrap Number,环绕号) 是事务 ID 的三个组成部分之一,完整的事务 ID 格式为:
事务 ID = (USN, Slot, Wrap#)
| 组成部分 | 含义 | 说明 |
|---|---|---|
| USN | Undo Segment Number | 回滚段编号,标识哪个回滚段 |
| Slot | Slot Number | 事务表中的槽位号(通常在 0~47 左右,取决于块大小) |
| Wrap# | Wrap Number | 槽位重用计数器,表示该槽位被循环使用了多少次 |
也就是说,Wrap#是槽位重用计数器 ,表示该槽位被循环使用了多少次 。其作用机制:
(1) 槽位复用 :当一个事务提交后,它占用的事务表槽位会被释放,后续的新事务可以重用这个槽位。
(2) Wrap# 递增 :每次一个槽位被新事务重用时,该槽位对应的 Wrap# 就会加 1。
(3) 区分历史事务:Wrap# 的存在是为了区分"曾经使用过同一个槽位的前序事务"和"当前正在使用该槽位的新事务"。即使 USN 和 Slot 相同,只要 Wrap# 不同,就是两个完全不同的事务。
可以使用以下SQL语句查看Wrap#
sql
select b.segment_name, b.tablespace_name
,a.ktuxeusn "回滚段编号"
,a.ktuxeslt "槽位号"
,a.ktuxesqn "Wrap#"
from x$ktuxe a, dba_rollback_segs b
where a.ktuxesqn > -429496730 and a.ktuxesqn < 0
and a.ktuxeusn = b.segment_id;
Wrap# 是一个 32 位无符号整数,最大值为:
0xffffffff = 4,294,967,295(约 42.9 亿)
对于事务率极高且数据库运行时间很长的系统:
-
回滚段中的槽位被频繁地分配、提交、再分配
-
每个槽位的 Wrap# 持续增长
-
最终可能接近甚至达到这个上限
当 Wrap# 接近最大值时,Oracle 在内部计算或比较事务版本时可能发生溢出或异常,从而触发:
- ORA-600 4187:内部错误,通常与事务 ID 的 wrap# 溢出有关
- ORA-1558:"out of transaction ID's in rollback segment",表示该回滚段的事务 ID 已耗尽
解决方案
- 重建undo表空间
- 升级到12.2.0.1之后的数据库版本。