ORA-01628 是 Oracle 数据库中一个经典的错误,通常表示 回滚段(Rollback Segment)或 Undo 段达到了最大扩展数(max extents),无法再分配新的区(extent)来存储事务的撤销数据。最近有个银行客户在跑业务时,遇到了ORA-01628. 这里简单记录分析思路。
错误信息示例
ORA-01628: max # extents 32765 reached for rollback segment ...
主要原因
- Undo 表空间不足或配置不当:
- 这是最常见的原因。当事务产生的撤销数据量很大,且持续时间很长时,如果 Undo 表空间的数据文件无法自动扩展,或者已经达到了最大文件大小限制,回滚段就会试图通过增加“区(extent)”的数量来获取更多空间。
- 一旦区的数量达到上限(旧版本通常是 505 或 1024,新版本自动管理的 Undo 段上限通常为 32765),就会报此错。
- 长事务或未提交的事务:
- 如果有长时间运行的事务(如大批量更新、删除操作)未提交,它们会一直占用 Undo 空间,阻止空间回收,导致其他事务无法获取足够的 Undo 空间而触发此错误。
- Oracle 10g/11g 的 Automatic Tuning of Undo Retention 特性 Bug:
- 在特定的早期版本(如 10.2.0.1.0)中,存在一个已知问题:自动调整 Undo 保留时间的算法可能有缺陷,导致系统认为需要保留大量的“未过期(unexpired)”Undo 数据,从而迅速耗尽回滚段的扩展能力。
- This is Bug 6475502
Abstract: ORA-1628 MAXEXTENTS REACHED USING AUM ON LOCALLY MANAGED TABLESPACE
- 手动管理回滚段(较少见):
- 如果在非常旧的系统中使用手动管理的回滚段,且
MAXEXTENTS参数设置得太小,也容易触发此问题。现代数据库默认使用自动管理的 Undo 表空间。
- 如果在非常旧的系统中使用手动管理的回滚段,且
分析思路
1, 查看undo表空间使用率,增加表空间不一定解决
select a.tablespace_name, a.file_name, round(a.bytes/1024/1024,0) sbytes,
round(sum(b.bytes/1024/1024),0) fbytes, count(*) kount, autoextensible
from dba_data_files a, dba_free_space b
where a.file_id = b.file_id
and a.tablespace_name in (select z.tablespace_name from dba_tablespaces z where retention like '%GUARANTEE')
group by a.tablespace_name, a.file_name, a.bytes, autoextensible
order by a.tablespace_name
/
2,检查undo retentions是否太长
如果是因为保留时间设置过长导致空间无法回收,可以适当减小该参数(单位:秒)。
show parameter undo
select segment_name,
nvl(sum(act),0) "ACT BYTES",
nvl(sum(unexp),0) "UNEXP BYTES",
nvl(sum(exp),0) "EXP BYTES"
from (
select segment_name,
nvl(sum(bytes),0) act,00 unexp, 00 exp
from DBA_UNDO_EXTENTS
where status='ACTIVE' group by segment_name
union
select segment_name,
00 act, nvl(sum(bytes),0) unexp, 00 exp
from DBA_UNDO_EXTENTS
where status='UNEXPIRED' group by segment_name
union
select segment_name,
00 act, 00 unexp, nvl(sum(bytes),0) exp
from DBA_UNDO_EXTENTS
where status='EXPIRED' group by segment_name
) group by segment_name;
select to_char(begin_time,'MM/DD/YYYY HH24:MI') begin_time, UNXPSTEALCNT, EXPSTEALCNT , NOSPACEERRCNT, TUNED_UNDORETENTION from gv$undostat;
3, 检查长事务/大事务
找出占用大量 Undo 空间且长时间未提交的事务,视情况杀掉会话或督促其提交。
SELECT s.sid, s.serial#, s.username, u.segment_name, count(u.extent_id) "Extent Count", t.used_ublk, t.used_urec, s.program
FROM v$session s, v$transaction t, dba_undo_extents u
WHERE s.taddr = t.addr and u.segment_name like '_SYSSMU'||t.xidusn||'_%$' and u.status = 'ACTIVE'
GROUP BY s.sid, s.serial#, s.username, u.segment_name, t.used_ublk, t.used_urec, s.program
ORDER BY t.used_ublk desc, t.used_urec desc, s.sid, s.serial#, s.username, s.program;
4, 检查UNDO段是否碎片严重
SELECT segment_name, bytes/1024 "Extent_Size_KB", count(extent_id) "Extent_Count", bytes * count(extent_id) / power(1024, 2) "Extent_MB"
FROM dba_undo_extents
WHERE segment_name = '_SYSSMU3xxxx031$'
group by segment_name, bytes
order by 1, 3 desc;
SEGMENT_NAME Extent_Size_KB Extent_Count Extent_MB
------------------------------ -------------- ------------ -----------
_SYSSMU375_247595031$ 64 31858 1,991.13
_SYSSMU375_247595031$ 8192 493 3,944.00
select segment_name,
round(nvl(sum(act),0)/(1024*1024*1024),3 ) "ACT GB BYTES",
round(nvl(sum(unexp),0)/(1024*1024*1024),3) "UNEXP GB BYTES",
round(nvl(sum(exp),0)/(1024*1024*1024),3) "EXP GB BYTES",
NO_OF_EXTENTS
from ( select segment_name, nvl(sum(bytes),0) act,00 unexp, 00 exp, count(*) NO_OF_EXTENTS
from DBA_UNDO_EXTENTS
where status='ACTIVE' and tablespace_name = 'UNDOTBS4'
group by segment_name
union
select segment_name,00 act, nvl(sum(bytes),0) unexp, 00 exp , count(*) NO_OF_EXTENTS
from DBA_UNDO_EXTENTS
where status='UNEXPIRED' and tablespace_name = 'UNDOTBS4'
group by segment_name
union
select segment_name, 00 act, 00 unexp, nvl(sum(bytes),0) exp, count(*) NO_OF_EXTENTS
from DBA_UNDO_EXTENTS
where status='EXPIRED' and tablespace_name = 'UNDOTBS4'
group by segment_name
) group by segment_name, NO_OF_EXTENTS having NO_OF_EXTENTS >= 30 order by 5 desc;
Note: UNDO AUTO管理时,无法人为控制,很可能再次查询时已合并或释放。
如果在一个时间周期内,如果事务大,会需要大量的undo空间,有比较大的undo retention time, 长时间的undo block无法过期,需要rollback segment增长,如果undo segment 不多(参数或表空间限制),会倾向于在undo segment上分配更多的extent, 出现该错误。
使用自动撤销功能时,您无法控制区段大小,也无法缩小区段。这一切都是自动完成的。系统会自动决定区段大小,但如果undo段扩展过大,通常会分配更大的区段。回滚段中区段数量过多很可能是由于undo表空间碎片造成的:由于碎片问题,Oracle 可能只能分配 64KB 的区段,因此很可能达到最大区段数限制。
undo段的最大区段数限制为 32KB,长时间运行的大型事务可能会通过添加新区段来耗尽此限制(如果当前区段的下一个区段尚未过期),最终导致 ORA-1628 错误。
因此,在事务将撤销段扩展到其限制后,如果出现 ORA-1628 错误,则后续事务将无法绑定到该撤销段,直到该段被缩小(您可能会看到区段数量减少)。
因此,导致 ORA-1628 问题的两个主要原因是事务过大或撤销表空间碎片化。
对于过大的事务,可以通过将大事务拆分成较小的事务来解决(例如,频繁提交)。
对于撤销表空间碎片化,可以通过重建撤销表空间来解决(这也是针对同一问题提交的 Bug 10330444 和 Bug 10229998 的推荐解决方案,这两个 Bug 已被标记为非缺陷并关闭)。
如果分配了许多小尺寸的扩展区,请应用补丁 17306264,并设置:
event=”64000 trace name context forever, level 25″
此补丁包含在 12.1 及更高版本中。
此补丁中的事件控制着每个undo segment的extents分配。一旦 8KB 的extent被分配到任何undo segment,next extent分配将在undo表空间的可用空间中查找大小为 1MB 或其倍数的extent,并将其分配到需要扩展的同一undo segment。
此外,请确保同时增加undo表空间的大小,因为设置上述事件后将分配更大的undo extents。