结论:
此时,查询该块时发生了块清除,将锁定信息清除,oracle之所以会这么做是因为session 1的select语句这个事务比回滚端中已提交事务中最小的scn要大(实际上它的scn最大,不考虑其他事务情况),但是之前的commit事务(产生延迟块清除的事务)信息由于UNDO端在这期间的大量commit事务已经被覆盖,具体的scn已经无法获得,oracle指定了一个UNDO段中最小的scn作为commit的scn,这个scn肯定要比之前提交的事务的scn要大,尽管不准确,但是不会影响select查询,这个scn称为upper bound。
利用这个scn来更新块的ITL,完成块清除。
但如果select事务的scn在UNDO端中也无法获得(被覆盖)的时候,即select事务的scn要比UNDO段中最小的scn还要小,oracle无法判断select scn与commit scn,就会报错ORA-01555。
不过由于延迟块清除造成的快照过久,条件比较高,我在测试的时候也很难达到,需要一个长查询,许多的事务,UNDO空间回绕,select scn被覆盖等条件,所以在现实情况中很少见。
下面是测试由于延迟块清除产生的ora-01555:
在一个session中执行:
SQL> alter session set isolation_level=serializable;
Session altered.
(注:之所以修改session模式,是为了模拟一个长查询,serializable session中的查询结果是事务开始的那一刻数据样子,而不是查询开始)
在另一个session中执行:
SQL> update tab_clean set a=a+1;
10000 rows updated.
SQL> commit;
Commit complete.
(注意:在commit前需要将buffer_cache清空)
此时,tab_clean表中所有的块都没有清除,再执行大量的小事务,让UNDO回绕:
SQL> begin
2 for i in 1 .. 5000 loop
3 update tab_itl set a = a + 1;
commit;
end loop;
end; 4 5 6
7 /
begin
*
ERROR at line 1:
ORA-30036: unable to extend segment by 8 in undo tablespace 'UNDOTBS1'
ORA-06512: at line 3
该报错可以忽略,说明UNDO端已经满了,之前提交的信息已经被覆盖,再回到serializable那个session,执行:
SQL> select count(*) from tab_clean;
select count(*) from tab_clean
*
ERROR at line 1:
ORA-01555: snapshot too old: rollback segment number 2 with name "_SYSSMU2$"
too small
此时,再看一下tab_clean表中块的情况
SQL> alter system dump datafile 5 block 339719;
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0002.022.000017d3 0x00800017.776d.18 ---- 1 fsc 0x0000.00000000
0x02 0x000a.00f.0000177a 0x00800056.5313.18 C-U- 0 scn 0x0002.2a5795d5
块还是锁定的,在其他
session
中再次查询该块就可以对块进行清除了。