问题背景:
近期遇到了一个Oracle回滚段事务ID达到上限的问题,应用前台语句操作失败,出现ORA-01558: out of transaction ID's in rollback segment _SYSSMU10_4119033733$报错。
问题分析:
第一次遇到该报错,先到Oracle mos上查了一下ORA-01558报错的相关资料,找到了一个比较接近的案例,MOS ID:Bug 19700135 - ORA-600 [4187] when the undo segment wrap# is close to the max value of 0xffffffff (Doc ID 19700135.8),根据文章的描述,报错产生的原因是由于回滚段里面的事务槽由于事务的频繁申请,导致wrap#达到上限值0xffffffff(4294967295),致使事务申请失败,事务槽每分配给一个事务,wrap#就会递增,每次的递增值并不固定,但单次递增值不超过16。
文档还给出了一条查询语句,用户确认当前回滚段是否出现wrap#达到了上限值的问题,通过查询undo基表x$ktue([K]ernel [T]ransaction [U]ndo transaction [E]ntry),找出wrap#在[-429496730,0]区间的回滚段
select b.segment_name, b.tablespace_name
,a.ktuxeusn "Undo Segment Number"
,a.ktuxeslt "Slot"
,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;
在报错的数据库执行该查询语句,可以看到查询出来回滚段的确为报错回滚段_SYSSMU10_4119033733$,但这里为啥使用这个范围条件[-429496730,0]进行查询,包括查出来wrap#的数量含义,哪个事务槽有问题,文章并没有进行详细说明,为此,我对回滚段的wrap#上限值进行了研究测试。
回滚段事务槽warp#上限研究测试:
我们以UNDOTBS1表空间里面的一个回滚段_SYSSMU3_2471395754作为研究测试对象,dump出了回滚段的头块。
---使用dump undo header
alter system dump undo header '_SYSSMU3_2471395754$';
---也可以通过dba_segments查出回滚段的头块ID,再通过dump datafile方式
alter system dump datafile 3 block 128;
分析dump的回滚段头信息,单个回滚段里面34个事务槽,从0开始,每一个事务槽都有各自对应的wrap#大小,这里我们将研究测试的对象缩小到0号槽0x00,当前的wrap#为0x0fae,转成10进制为4014。
再通过x$ktue查询该回滚段的wrap信息,可以看到列ktuxeslt和ktuxesqn分别对应事务槽slot和wrap#,查到到数值为0和4014,与dump回滚段头信息一致。
select b.segment_name, b.tablespace_name
,a.ktuxeusn "Undo Segment Number"
,a.ktuxeslt "Slot"
,a.ktuxesqn "Wrap#"
from x$ktuxe a, dba_rollback_segs b
where a.ktuxeusn = b.segment_id and b.segment_name='_SYSSMU3_2471395754$';
使用bbed打开回滚段所在的块,通过find命令查询事务槽的offset,需要注意的是对于Linux,由于字节序是little-endian,所以要低位开始读取,比如dump文件里面的wrap#为00000fae,在bbed显示的顺序为ae0f0000,所以查找wrap#的位置通过find /x ae0f,查到0号槽的wrap#的offset为6200-6203。
通过bbed修改0号槽的wrap#的offset为上限值ffffffff,模拟wrap#达到上限值。
再次查看x$ktue里面回滚段0号槽的wrap#大小,可以看到wrap#显示的值为-1。
通过set transaction user rollback segment方式测试事务指定使用wrap#为-1槽所在的回滚段,看看会出现什么样的问题。
begin
for i in 1..100000 loop
set transaction USE ROLLBACK SEGMENT "_SYSSMU3_2471395754$";
delete from test.test where rownum<2;
commit;
end loop;
end;
/
可以看到操作会话直接异常断开ORA-03113,并且后台日志出现大量的ORA-00600: internal error code, arguments: [4194], [], [], [], [], [], [], [], [], [], [], []报错,语句无法正常执行。
通过bbed重新修改wrap#为fffffffa(4294967290)离上限值只差距5。
再次查看x$ktue里面回滚段0号槽的wrap#大小,可以看到wrap#显示的值为-6。
再次通过set transaction user rollback segment测试事务指定使用wrap#为-6的回滚段,可以看到这一次复现了ORA-01558: out of transaction ID's in rollback segment _SYSSMU3_2471395754$的报错。
再次查看x$ktue里面回滚段0号槽的wrap#大小值为-2,没有再变化,接近最大值ffffffff。
修改wrap#为最大值的一半:7fffffff,可以看到x$ktue里面显示的值为2147483647。
修改wrap#为最大值的一半加1:80000000,可以看到x$ktue里面显示的值为-2147483647。所以,x$ktue里面显示的wrap#的值范围将ffffffff且为了两段,第一段[0,2147483647],第二段[-2147483647,-1]。wrap#越接近-1的代表该事务槽slot接近上限。
这时候我们再看回mos文档给的查询语句,a.ktuxesqn > -429496730 and a.ktuxesqn < 0查询条件的含义就是查询wrap#的值范围接近ffffffff上限的事务槽
而之前问题查到的结果回滚段_SYSSMU10_4119033733$事务槽里面slot 14的wrap#为-17,接近上限值-1,就是导致出现ORA-01558: out of transaction ID's in rollback segment _SYSSMU10_4119033733$报错的原因。
问题修复:
对于回滚段事务槽达到上限的,可以通过重建undo表空间的方法进行修复。
注:重启数据库,不会导致回滚段wrap#清空。
重建undo表空间的操作步骤。
--创建新的undo表空间
SQL> create undo tablespace <New Undo Tablespace name> datafile size <new size>;
--设置当前的默认表空间为新undo表空间
SQL> alter system set undo_tablespace=<New Undo Tablespace name>;
--查看旧undo表空间是否存在online的回滚段,即使切换了默认的undo表空间,已经被事务分配使用的旧undo回滚段依然在使用,直到事务提交之后才释放
SQL> select status,segment_name from dba_rollback_segs where status not in ('OFFLINE') and tablespace_name=<undo tablespace to be dropped>;
--需要等待旧undo表空间没有online的回滚段才能drop 旧的undo表空间
SQL>Drop tablespace <tablespace_name> including contents and datafiles;