有时候我们在执行select操作的时候也可能会产生redo,其一个可能原因就是oracle块清除。
在进行块清除的时候,如果是一个大事务,就会进行延迟块清除块清除就是删除所修改数据块上与”锁定”有关的信息,即事务信息
Oracle在事务相关的提交列表中,记录下已修改的块列表,每个列表记录20个块,根据需要可能分配有多个这种列表.
这种块列表有一个上限,就是缓冲区缓存大小的10%.
如果一次修改的块,没有超过了缓冲区缓存大小的10%,并且这些块在内存中,则commit时,会清除块上的事务信息,
否则,就不会理会它,直到下次访问这些块时,再清除块中的事务信息,这就是延迟块清除.
因为这个Select修改了块的事务信息,所以就会产生Redo.
关于块清除时SCN的填写,以及什么情况产生”快照太旧”的错误:
延迟清除的块的下一个读者,首先根据块中的记录的回滚信息去查找回滚段中记录的commit时的SCN,
但回滚段可能已回绕,找不到提交时的scn了,
但是,从回滚段中可以得到一个最小的提交scn并且该事务已经提交肯定小于这个从回滚段中还存在的最小scn。
那么oracle给这个块清除的事务分配一个从回滚段中找到的最小事务scn。
这虽然不准确,但是是安全的,对于数据访问也不构成影响。所以叫 upper bound ,猜测的一个scn的上限。
延迟清除的块在被select 时,如果读的select 的scn 比这个回滚段里面最小的scn 还要小的话(回滚段已回绕),那么在回滚段里面找不到数据了,oracle 就没有办法判断select 的SCN 与被要清除的数据块的大小关系,于是ora-01555就出现了,这个时候oracle 就不知道数据块里面的数据是不是是查询时刻需要的数据.
如果select scn 大于回滚段里面最小的scn 的话,那么oracle 就使用这个最小的scn 来做为这个事务的 scn 来更新块的itl ,从而完成块的清除.
除了本文文中方法,还有一种方法:
非IMU模式下可测:会话1:修改 会话2:做完全检查点 alter system checkpoint; 清空数据缓存区alter system flush buffer_caache; 再查询
实验一:延迟块清除的产生
1.先构建一个1万条数据的表
create table test as select * from dba_objects where rownum<10001;
2.使用循环来插入数据。
初始数据1W条,则每次循环后表内数据量依次是2,4 ,8 ,16 ,32 ,64 ,128, 256,512,1024,需要10次循环。
begin
for i in 1 .. 10 loop
insert into test select * from test;
commit;
end loop;
end;
3.执行查询,执行计划中可以看到
2204 redo size
SQL> select count(*) from test;
COUNT(*)
----------
5120000
SQL> select count(1) from dba_indexes where table_name='TEST';
COUNT(1)
----------
0
SQL> alter system flush buffer_cache;
System altered.
SQL> set autotrace on
SQL> select count(*) from test where object_name like '%gc%';
COUNT(*)
----------
0
Execution Plan
----------------------------------------------------------
Plan hash value: 1950795681
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 66 | 18588 (1)| 00:03:44 |
| 1 | SORT AGGREGATE | | 1 | 66 | | |
|* 2 | TABLE ACCESS FULL| TEST | 751 | 49566 | 18588 (1)| 00:03:44 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("OBJECT_NAME" IS NOT NULL AND "OBJECT_NAME" LIKE '%gc%')
Note
-----
- dynamic sampling used for this statement (level=2)
Statistics
----------------------------------------------------------
5 recursive calls
0 db block gets
101073 consistent gets
67716 physical reads
2204 redo size
421 bytes sent via SQL*Net to client
419 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
实验二:延迟块清除的避免
因为延迟块清除有可能会导致ORA-01555错误(操作大数据时),所以应该避免。
在操作大数据量的DML操作后做一个select 操作。
SQL> create table test as select * from dba_objects where rownum<10001;
Table created.
SQL>
declare
v_select clob;
begin
for i in 1 .. 5 loop
insert into test select * from test;
commit;
select count(*) into v_select from test;
end loop;
9 end;
10 /
PL/SQL procedure successfully completed.
SQL> set autotrace on;
SQL> alter system flush buffer_cache;
System altered.
从查询结果中可以看到, 0 redo size,没有产生REDO。
SQL> select count(*) from test where object_name like '%gc%';
COUNT(*)
----------
0
Execution Plan
----------------------------------------------------------
Plan hash value: 1950795681
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 66 | 1134 (1)| 00:00:14 |
| 1 | SORT AGGREGATE | | 1 | 66 | | |
|* 2 | TABLE ACCESS FULL| TEST | 46 | 3036 | 1134 (1)| 00:00:14 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("OBJECT_NAME" IS NOT NULL AND "OBJECT_NAME" LIKE '%gc%')
Note
-----
- dynamic sampling used for this statement (level=2)
Statistics
----------------------------------------------------------
5 recursive calls
0 db block gets
4212 consistent gets
4363 physical reads
0 redo size
421 bytes sent via SQL*Net to client
419 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed