Oracle ORA-1628错误的定位分析(2012/1/8)

在Oracle 10.2.0.1.0中,只要设置了undo表空间自动管理,不管有没开启自动扩展,不管undo_retention设置为多少,都会启用 Automatic Tuning of undoretention的新特性。这个新特性中计算RETENTION算法有问题,会导致unexpired undo数据奇高,并最终导致出现ORA-1628: max # extents 32765reached for rollback segment错误。

1 问题现象

Oracle版本:10.2.0.1.0。

浙江现场NQA在执行update/insert操作失败,ORACLE返回ORA-1628错误,并且ORACLE的alert日志中ORA-1628出现的非常频繁。错误日志:

Update fail,msg::ORA-01628: max # extents (32765) reached for rollback segment _SYSSMU7$

2 分析

我们在alert日志中发现了ORA-30036错误,出现ORA-30036表示UNDO表空间太小。

ORA-30036的错误描述:

Update fail,msg::ORA-30036: unable to extend segment by 8 in UNDOtablespace 'UNDOTBS1'

事务申请undo空间的流程如下:

1)  事务启动时,ORACLE会为之分配一个undo segment。ORACLE的设计目标是一个事务对应一个undo  segment。ORACLE启动时,只使得部分undo segments变为online。随着事务数量的增多,更多的offline UNDOsegments变为online状态。如果所有undo segment已经是online状态,且undo表空间还有足够的空间,oracle会创建新undo segment;如果undo表空间的空间已经用尽,几个事务会合用一个undo segment。

2) 检查事务对应的undo segment是否有空闲区(extent),如果有直接使用。
3) 如果没有,使用本segment中处于expired状态的extent,这些extent存储了由其它事务提交或回滚留下的过期数据。这个动作称为一次扩展(wrap),计入v$rollstat的wraps列。
4) 如果本回滚段中没有可用的区,从UNDO表空间中请求区。这被称为一次Extend,记入v$rollstat的EXTENTS列。
5)  UNDO表空间中没有Free区,从其他回滚段Steal过期的区。Steal的单位一定是以区为单位。无论Steal是否成功,V$UNDOSTAT的EXPSTEALCNT列都会加1,此列统计的是次数。EXPBLKRELCNT列是成功Steal的块数。
6) 如果Steal过期区不成功,试图扩展数据文件。
7)  如果无法扩展文件,在本回滚段中重用未过期的区,        V$UNDOSTAT.UNXPBLKREUCNT列增加(增加值是区中块的个数)
8) 如本回滚段无过期的块,则Steal其他回滚段中未过期的区。每Steal一次,无论是否成功,都会记入V$UNDOSTAT.UNXPSTEALCNT列。Steal的块数记入NXPBLKREUCNT列。
9) 如还不行,报ORA-30036错误。

于是,创建新的undo 表空间UNDOTBS2,UNDOTBS2包含了4个数据文件,每个文件有31GB。然后切换至新的undo表空间。为什么数据文件大小是31G,在block大小为8K的情况下,数据文件大小限制是32G,有人说数据文件变为32G后会出现bug,为了避免这种情况,于是设置数据文件大小为31G。

【切换undo的表空间的代码如下】

--查看当前的undo表空间

show parameter undo_tablespace;

--查看所有undo表空间

select * from  dba_tablespaces where contents='UNDO';

--查看当前undo表空间对应的文件

select * from dba_data_files wheretablespace_name='UNDOTBS1';

--创建新undo表空间注意修改大小!!!!!!!

create undo tablespace UNDOTBS2datafile

 '/opt/oracle/oracle/oradata/orcl/undotbs02_1.dbf' size 31G autoextend off,

 '/opt/oracle/oracle/oradata/orcl/undotbs02_2.dbf' size 31G autoextend off,

 '/opt/oracle/oracle/oradata/orcl/undotbs02_3.dbf' size 31G autoextend off,

 '/opt/oracle/oracle/oradata/orcl/undotbs02_4.dbf' size 31G autoextend off,

 extent management local autoallocate retention nogurantee;

--确认是否创建成功

select * from  dba_tablespaces where contents='UNDO';

--切换当前表空间

alter system setundo_tablespace=UNDOTBS2 scope=both;

--确认UNDOTBS1上没有活动的事务,若有,则等待

select usn,xacts,status from v$rollstatwhere xacts != 0;

--删除旧undo表空间

drop tablespace UNDOTBS1 includingcontents and datafiles cascade constraints;

 切换undo表空间4个小时后,出现了一次ORA-01555错误,日志如下:

ORA-01555 caused by SQLstatement below (SQL ID: 0qdadgcckc5xd, Query Duration=1233 sec, SCN:0x0001.c06cbc64):
Fri Dec 28 16:15:51 2012 select tavppfr0_.UMFAILUREREASON as col_0_0_,count(*) as col_1_0_ from tAVP_PFR tavppfr0_ wheretavppfr0_.UMEVENTTIMESTAMP>=1356652518561 group by tavppfr0_.UMFAILUREREASON
Fri Dec 28 16:16:57 2012

ORA-01555是ORACLE中非常常见的错误。在undo表空间切换后,旧的undo表空间变为offline,如果事务访问旧undo表空间的数据就会得到ORA-01555错误。这是因为offline的undo表空间可能包含了为达到事务一致性所必需的数据。所以切换表空间的短时间内出现ORA-01555错误可以忽略。

悲催的是,切换undo表空间8个小时后出现ORA-01628错误。难道是UNDO表空间又满了? 于是查看处于undo空间分配情况,从表dba_undo_extents可以发现处于active/unexpired/expired状态的undo数据分别为81M/35G/1M。

>select tablespace_name,status,SUM (bytes) fromdba_undo_extents GROUP BY tablespace_name,status;

TABLESPACE_NAME                STATUS    SUM(BYTES)

UNDOTBS2                       EXPIRED      1114112

UNDOTBS2                       UNEXPIRED 3.5858E+10

UNDOTBS2                       ACTIVE      81002496

然后查看了最大和最小的undosegment。最小undosegment只有4个区(extent),只占用了1M空间,而最大的undo segment中的区(extent)数量已经达到了其最大值32765。

>select  SEGMENT_NAME,TABLESPACE_NAME, BYTES/1024/1024, EXTENTS,MAX_EXTENTS from dba_segments whereTABLESPACE_NAME='UNDOTBS2';

SEGMENT_NAME TABLESPACE_NAMEBYTES/1024/1024  EXTENTS MAX_EXTENTS

_SYSSMU84$   UNDOTBS2        24738.6875       32765 32765

_SYSSMU104$  UNDOTBS2        1.1875           4    32765

到这里,我感到非常困惑。困惑点一,undo表空间是非常富余的,为什么有的undo segment的extents到达了极限32765,而有的undosegment仅仅包含4个extent。困惑点二,UNEXPIRED状态的undo数据远远大于其它状态的数据量。难道有大事务正在回滚,因为我测试发现,事务回滚后,其undo 数据会变为unexpired状态。通过查询x$ktuxe视图,发现回滚事务占用undo空间仅为338*block_size=2.5M。注意操作x$ktuxe需要使用sys用户。

>select sum(ktuxesiz) from x$ktuxe whereKTUXESTA='ACTIVE';

SUM(KTUXESIZ)

-------------

          338

可不可以通过修改undosegment的MAXEXTENTS属性,从而避免这个错误。执行后发现UNDO_MANAGEMENT处于AUTO模式下,无法修改器storage参数的。

>alter rollback segment "_SYSSMU84$" storage(MAXEXTENTS 40000);
ORA-02221: invalid MAXEXTENTS storage option value

难道是UNDO_RETENTION调整的非常大导致处于unexpired状态的数据异常的大?通过查看v$undostat发现最新记录的TUNED_UNDORETENTION字段仅为900秒,并且在创建undo表空间指定了retention nogurantee,所以这个原因也排除了。

>slect begin_time,end_time from v$undostat order bybegin_time desc;

至此,一切陷入僵局。只能继续从业务下手,通过每隔30秒采集数据来找出大事务。脚本见第4小节。一天后发现,最大的事务也只占用了1G的undo空间。于是开始强烈怀疑是ORACLE自身的bug所至,google一下后发现了ORACLE的10G版本在计算RETENTION算法有bug。于是试着使用其内部参数_undo_autotune关闭其自动调整RETENTION,即执行如下语句。

>alter system set "_undo_autotune" = falsescope=both;

执行上述操作900秒后发现,unexpired状态从35G下降至169M,且_SYSSMU84$的数量从32765下降到了132。

>select tablespace_name,status,SUM (bytes) fromdba_undo_extents GROUP BY tablespace_name,status;

UNDOTBS2                       EXPIRED       232.75                                                                                                                                                 

UNDOTBS2                       UNEXPIRED  16957.625                                                                                                                                                 

UNDOTBS2                       ACTIVE       61.9375   

>select  usn,extents from V$ROLLSTAT;

--只贴出了部分记录

USN EXTENTS

84  132

至此可以确认,ORACLE的计算retention算法是有bug的。后来咨询了ORACLE的售前工程师,答复如下。

Oracle 10gr2的后续版本中添加了撤销(UNDO)信息最短保留时间段自动调优的特性,不再仅仅依据参数UNDO_RETENTION的设定,(需注意10.2.0.3以下有BUG,10gR2的bug现象为,只要设置了undo表空间自动管理,不管有没开自动扩展,不管undo_retention设置为多少,都会启用 Automatic Tuning of undo retention的新特性。这个bug在10.2.0.4中已经修复,建议找时间停机打patch)其调优原则如下:

当撤销表空间(UNDO TABLESPACE)大小固定,Oracle将根据表空间的大小和实际的系统负载动态调整撤销信息保存时间,该最短保存时间的具体长短基于撤销表空间大小的一定比例值公式换算后获得;它总是比设定的UNDO_RETENTION大,当撤销表空间大量空闲情况下可能远远大于UNDO_RETENTION。

当撤销表空间设定为自动扩展空间情况下,Oracle将动态调整撤销信息最短保留时间为该时段最长查询时间(MAXQUERYLEN)加上300秒或参数UNDO_RETENTION间的较大者,即MAX((MAXQUERYLEN+300),UNDO_RENTION);同样的,该最短保存时间可能远远大于设定的UNDO_RETENTION。

在自动调整情况下,实际的撤销信息最短保留时间可以通过查询V$UNDOSTAT视图上的TUNED_UNDORETENTION列获得,在无法就撤销表空间做相应修改的情况,我们可以通过修改隐式参数” _UNDO_AUTOTUNE”为FALSE关闭该自动调优特性。以上设定生效后,V$UNDOSTAT视图上TUNED_UNDORETENTION列不再更新,且撤销信息最短保留时间固定为参数UNDO_RETENTION的设定值。该参数可以不用重启数据库而动态设置生效。

3 结论

在Oracle 10.2.0.1.0中,只要设置了undo表空间自动管理,不管有没开自动扩展,不管undo_retention设置为多少,都会启用 Automatic Tuning of undoretention的新特性。这个新特性中计算RETENTION算法有问题,会导致unexpired undo数据奇高,并最终导致出现ORA-1628: max # extents 32765reached for rollback segment错误。可以通参数_undo_autotune来关闭这个新特性,从而避免问题的产生。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章