首页 > 数据库 > Oracle > Ora-01555问题分析
当一个较长的查询执行时,查询数据过程中数据块发生了修改,并且被commit后事物量较大,undo segment被覆盖,该查询检索到被修改数据块时,为了一致性读需要去undo中查询数据块前镜像,而该undo segment已经被覆盖所以无法找到数据,此时就会报出ora-01555错误。
当数据块被修改后存放到buffer cache中,commit之前进行块清除时使用了延迟块清除(需要清除的数据块较多,超出了buffer_cache的10%,或者在commit之前,已经有dbwr进程将buffer_cache中的数据写入dbwr,比如commit前刷新了buffer cache),被修改的数据块通过dbwr写入数据文件,而每个事务被commit之后会有一个commit scn记录在undo里,select语句发出时会有一个select scn,如果发现select检索的数据块是被延迟块清除的,会通过ITLl事物槽到undo里找对应的该数据库的commit scn,如果此时undo已经被刷新,找不到对应的该数据库的scn,会拿select scn与undo里的最小的commit scn对比,一般来说select scn都会比undo 最小commit scn大,所以会把undo中最小commit scn作为被延迟块清除的数据库的commit scn,这样虽然有真正的该数据库的commit scn不一致,但数据是一直的,所以还是安全的。但是如果真出现比较极端的,select scn比undo里最小的commit scn还要小,那就出现ora - 01555的错误了。
为了方便演示过程,临时修改一下undo的参数:
SQL> create undo tablespace undotbs02 datafile '/u01/app/oracle/oradata/orcl_dup/undotbs002' size 5m autoextend off;
Tablespace created.
SQL> alter system set undo_tablespace=undotbs02 scope=spfile;
System altered.
SQL> alter system set undo_retention=3 ;
System altered.
undo_retention参数用来控制undo数据的保留时间,默认的是900s,此处改为3s只是为了方便测试,更加详细的讲解请参照http://blog.itpub.net/17203031/viewspace-774498
10203事件用来跟踪块的清除操作
SQL> alter system set event='10203 trace name context forever' scope=spfile;
System altered.
SQL> shutdown immediate;
Database closed.
Database dismounted.
ORACLE instance shut down.
SQL> startup
ORACLE instance started.
Total System Global Area 167772160 bytes
Fixed Size 1218316 bytes
Variable Size 62916852 bytes
Database Buffers 100663296 bytes
Redo Buffers 2973696 bytes
Database mounted.
Database opened.
SQL> show parameter undo;
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
undo_management string AUTO
undo_retention integer 3
undo_tablespace string UNDOTBS02
SQL> conn scott/oracle
Connected.
SQL> create table t as select * from emp;
Table created.
SQL> select
2 rowid,
3 dbms_rowid.rowid_relative_fno(rowid)rel_fno,
4 dbms_rowid.rowid_block_number(rowid)blockno
5 from t
6 where rownum=1;
ROWID REL_FNO BLOCKNO
------------------ ---------- ----------
AAAOEDAAEAAAAIMAAA 4 524
/** 为了方便,scott用户会话不要中断,不然事务会自动回滚,以下测试中dump操作均使用新的会话 **/
打开一个游标,执行一个查询
SQL> conn scott/oracle
Connected.
SQL> var c1 refcursor
SQL> begin
2 open :c1 for select * from t;
3 end;
4 /
PL/SQL procedure successfully completed.
T表的第一行数据块物理位置为数据文件4数据库524,先把该数据块dump出来看看数据库的ITL状态,为了方便些了个简单sql组合
[oracle@node1 db_1]$ more dump_block.sql
alter system dump datafile 4 block 524;
oradebug setmypid;
oradebug tracefile_name;
SQL> @dump_block
System altered.
Statement processed.
/u01/app/oracle/admin/orcl_dup/udump/orcl_dup_ora_4786.trc
Itl状态如下,此时是没有锁定信息的
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0xffff.000.00000000 0x00000000.0000.00 C--- 0 scn 0x0000.002cce0f
0x02 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
0x03 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
更改该行数据,并再次dump出块信息,Itl(0x02)行被锁定
SQL> update t set sal=1234 where rownum=1;
1 row updated.
Itl Xid Uba Flag Lck Scn/Fsc
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0xffff.000.00000000 0x00000000.0000.00 C--- 0 scn 0x0000.002d0488
0x02 0x0005.02f.000005ae 0x00800189.05d0.04 ---- 1 fsc 0x0000.00000000
0x03 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
通过Xid可以找到相应的事务信息,0005为分配的回滚段号,02f是事务槽,00000152对应wrap
数据的lb被指定为0x2
tab 0, row 0, @0x1cdf
tl: 38 fb: --H-FL-- lb: 0x2 cc: 8
col 0: [ 3] c2 0d 23
col 1: [ 4] 74 65 73 74
col 2: [ 4] 74 65 73 74
col 3: [ 3] c2 4e 5a
col 4: [ 7] 78 72 07 11 0a 37 2c
col 5: [ 4] c3 02 18 2e
col 6: *NULL*
col 7: [ 2] c1 0b
找到回滚段,转存回滚端头部信息(使用上面的方式通过导出数据块的Xid也可以推算)
SQL> select * from v$rollname where usn=5;
USN NAME
---------- ------------------------------
5 _SYSSMU5$
或者通过v$transaction,v$session,v$rollname视图查找当前的回滚段也可以
SQL> alter system dump undo header "_SYSSMU5$";
System altered.
SQL> alter system dump undo header "_SYSSMU5$";
System altered.
SQL> oradebug setmypid;
Statement processed.
SQL> oradebug tracefile_name;
/u01/app/oracle/admin/orcl_dup/udump/orcl_dup_ora_7539.trc
2f事务槽状态为10此时已经被使用
index state cflags wrap# uel scn dba parent-xid nub stmt_num cmt
------------------------------------------------------------------------------------------------
0x2f 10 0x80 0x05ae 0x0002 0x0000.00000000 0x00800189 0x0000.000.00000000 0x00000001 0x00000000 0
ccott连接的会话提交update操作
SQL> commit;
Commit complete.
提交事务后再导出查看一下,事务槽状态已经变为9(非激活),分配提交的scn和cmt
index state cflags wrap# uel scn dba parent-xid nub stmt_num cmt
------------------------------------------------------------------------------------------------
0x2f 9 0x00 0x05ae 0xffff 0x0000.002d0701 0x00800189 0x0000.000.00000000 0x00000001 0x00000000 1405908462
此时导出数据块信息,Scn/Fsc(commit scn)变的跟undo 中的提交scn一致,并且此时数据的锁定状态并未清除
SQL> @dump_block
System altered.
Statement processed.
/u01/app/oracle/admin/orcl_dup/udump/orcl_dup_ora_4960.trc
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0xffff.000.00000000 0x00000000.0000.00 C--- 0 scn 0x0000.002d0488
0x02 0x0005.02f.000005ae 0x00800189.05d0.04 --U- 1 fsc 0x0000.002d0701
0x03 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
tab 0, row 0, @0x1ce0
tl: 37 fb: --H-FL-- lb: 0x2 cc: 8
col 0: [ 3] c2 0d 23
col 1: [ 4] 74 65 73 74
col 2: [ 4] 74 65 73 74
col 3: [ 3] c2 4e 5a
col 4: [ 7] 78 72 07 11 0a 37 2c
col 5: [ 3] c2 0d 23
col 6: *NULL*
col 7: [ 2] c1 0b
此时执行一个大一点的事务,将undo里的更新前的524的块信息覆盖
SQL> begin
2 for i in 1..10000 loop
3 update emp set sal=4000;
4 rollback;
5 end loop;
6 end;
7 /
PL/SQL procedure successfully completed.
此时使用之前open的cursor执行查询,将找不到undo中的前镜像
SQL> print :c1
ERROR:
ORA-01555: snapshot too old: rollback segment number 5 with name "_SYSSMU5$"
too small
no rows selected
同样的打开一个游标
SQL> var c1 refcursor
SQL> begin
2 open :c1 for select * from t where rownum=1;
3 end;
4 /
PL/SQL procedure successfully completed.
更新一条数据
SQL> update t set sal = 4321 where rownum=1;
1 row updated.
dump该数据块,事务已分配
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0xffff.000.00000000 0x00000000.0000.00 C--- 0 scn 0x0000.002dde90
0x02 0x0006.003.000005c2 0x00800060.03d8.21 ---- 1 fsc 0x0000.00000000
0x03 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
dump出回滚段段头,事务槽1e已经分配
SQL> alter system dump undo header '_SYSSMU6$';
System altered.
SQL> oradebug setmypid;
Statement processed.
SQL> oradebug tracefile_name;
/u01/app/oracle/admin/orcl_dup/udump/orcl_dup_ora_7675.trc
index state cflags wrap# uel scn dba parent-xid nub stmt_num cmt
------------------------------------------------------------------------------------------------
0x03 10 0x80 0x05c2 0x0000 0x0000.002dde97 0x00800060 0x0000.000.00000000 0x00000001 0x00000000 0
提交事务之前,刷新buffer_cache,使用dbwr进程将被修改的数据块写入数据文件,这样被写入的数据块就使用了延迟块缓存
SQL> alter system flush buffer_cache;
System altered.
SQL> alter system flush buffer_cache;
System altered.
SQL> alter system flush buffer_cache;
System altered.
提交scott的事务
SQL> commit;
Commit complete.
此时dump出该数据块,ITL并未清除
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0xffff.000.00000000 0x00000000.0000.00 C--- 0 scn 0x0000.002dde90
0x02 0x0006.003.000005c2 0x00800060.03d8.21 ---- 1 fsc 0x0000.00000000
0x03 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
可是此时事务槽已经标记物inactive
index state cflags wrap# uel scn dba parent-xid nub stmt_num cmt
------------------------------------------------------------------------------------------------
0x03 9 0x00 0x05c2 0xffff 0x0000.002de0a1 0x00800060 0x0000.000.00000000 0x00000001 0x00000000 1405911654
此时执行一个大的事务,覆盖undo中该块的前镜像
SQL> begin
2 for i in 1..2000 loop
3 update emp set sal=1000;
4 rollback;
5 end loop;
6 end;
7 /
PL/SQL procedure successfully completed.
SQL> /
PL/SQL procedure successfully completed.
SQL> /
PL/SQL procedure successfully completed.
此时如果对打开的cursor c1执行查询会报错:
SQL> print :c1
ERROR:
ORA-01555: snapshot too old: rollback segment number 1 with name "_SYSSMU1$"
too small
no rows selected
再次对该数据块进行访问后,会发现该数据块commit scn被更新,但是该commit scn对应的不再是上面自己的事务槽的scn,而是undo中最小commit scn
SQL> select sal from t where rownum=1;
SAL
----------
4321
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0xffff.000.00000000 0x00000000.0000.00 C--- 0 scn 0x0000.002dde90
0x02 0x0006.003.000005c2 0x00800060.03d8.21 C-U- 0 scn 0x0000.002de239
0x03 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
测试完毕,别忘了把参数改回来
SQL> alter system set undo_retention=1080 scope=both;
System altered.
SQL> alter system set undo_tablespace=undotbs scope=both;
System altered.
SQL> drop tablespace undotbs02 including contents and datafiles;
Tablespace dropped.
SQL> show parameter undo
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
undo_management string AUTO
undo_retention integer 1080
undo_tablespace string UNDOTBS
1、如果自己的undo是手动管理,尽量的是rollback segment设置的一样大。
2、尽量避免SQL的运行时间过长,通过建索引,避让全盘扫描等手段来尽量减少SQL的执行时间。
3、尽量确保网络通畅,由于网络问题带来的ora-01555让dba无奈了。。。
4、cursor尽量在使用时打开,正如上面的例子,cursor长时间打开而不操作,很容易导致ora-01555错误。使用后立即关闭。
5、如果正常情况下系统undo过多,可以考虑加大undo空间。
6、尽量使大的查询在备库上执行。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/29320885/viewspace-1223886/ ,如需转载,请注明出处,否则将追究法律责任。