TA的每日心情 | 慵懒 2015-1-8 08:46 |
---|
签到天数: 2 天 连续签到: 1 天 [LV.1]测试小兵
|
ORA-1555错误简单的说就是针对一个数据块产生一致读时发生了错误。一致读就是指ORACLE利用回滚段来临时重构一个和事务或查询开始时的 块状态相同的快照块的过程。如果一个块改变了多次,可能就会有多个快照块的。
一个事务或查询开始执行时,ORACLE会产生一个SCN来记录这个开始时刻点,这个SCN也就叫做SNAPSHOT SCN。ORACLE仅仅看基于SNAPSHOT SCN的快照记录。如果块中有活动的事务或BLOCK SCN> SNAPSHOT SCN时,就产生了一致读。如果是没有活动的事务但没有产生COMMIT SCN的块 ,先产生DELAY BLOCK CLEANOUT,再比较COMMIT SCN与SNAPSHOT SCN的大小,如果COMMIT SCN小于SNAPSHOT SCN则直接使用该块,否则要产生一致读。
产生ORA-1555的可能情况:
1 一个长时间运行的查询,并同时针对查询需要的块有DML处理(主要是update和delete)
2 当查询和插入并发时
3 延迟块清除
4 交叉fetch和commit
5 回滚段发生错误
情况1 一个长时间运行的查询,并同时针对查询需要的块有DML处理(主要是update和delete)
当一个查询开始之后,正好某一个update更改了其中的一个记录,当查询扫描到该记录时,就需要一致读,回滚空间中去查找原值。如果这个查询的时间非常长,而那条记录修改之后,很快就被提交了,导致回滚空间中的记录可以被清除,并且这个数据库事务本身也比较多,回滚空间的覆盖比较块,在查询结束之前,回滚段中的记录空间就被新的记录覆盖占用了,导致查询找不到原值,从而产生ORA-1555错误。
Solution:
1、业务控制,禁止对同一个表的长时间查询和更新处理同时进行,要分开执行
2、增大回滚段的大小
3、增加回滚段的个数
4、不使用OPTIMAL选项,已经被使用的空间就不会很快收缩回来,可以使commit之后的记录保持尽可能长的时间
5、推迟对DML语句的COMMIT
6、优化查询语句,比如并行查询,目的是减小查询的时间
7、为要查询的表建立只读SNAPSHOT,这样对表记录的修改就不会影响到查询,但该表不能是太大的表
情况2 当查询和插入并发时
一般情况下,当我们查询一个表,同时对这个表作插入,这时是不需要一致读的。当然这种情况下也不可能产生ORA-1555错误。
当作全表扫描的时候,oracle会扫描所有位于HWM之下的空间,而对于HWM之上的空间将忽略。也正因为如此,当我们插入的数据位于HWM之下的时候,如果同时还有例如全表扫描这部分空闲的空间,就会产生一致读,就有可能会导致ORA-1555错误。 特别是在VLDB和OPS系统中,因为在这些系统中,很多时候都会对表作pre-allocate,当作了预分配之后,如果作一次大批量的delete,将会有非常多的可用空间位于HWM之下。
Solutions:
1、情况1的所有解决方法这里同样适用
2、不要预分配区(pre-allocate extent)
3、使用tuncate来删除表记录,使HWM收缩。
4、使用直接路径选项导入数据,使插入的数据在HWM之上。
情况3 延迟块清除
关于延迟块清除的概念,可以参考后面的附录。
Solution:
1、使用oracle并行服务器OPS,将DML操作分割到不同的实例上执行
2、在批处理中使用SET TRANSACTION USE ROLLBACK SEGMENT为事务单独指定一个回滚段。
3、可以在DML之后,立即执行一个全表扫描和analyze indexes来强制立即进行块清除。
情况4 交叉fetch和commit
这种比较典型的情况是在一个过程中使用了如下的结构:
cursor c1 is select * from bigemp;
begin
for c1rec in c1 loop
update mydual set a=a;
commit;
end loop;
end ;
Solution:
1、检查过程,避免这种交叉提取和提交的情况出现。
2、延迟commit
3、在查询语句中,增加“ order by 1 ”的语句,这样会在临时段中保留ORDER BY的结果,可以避免一些一致读。
情况5 回滚段发生错误的解决方法:
由这种原因导致的ORA-01555错误是极少数的。一般情况下,也只有在那些不支持大文件的操作系统会发生这种情况。
Solution:
1、检查init.ora中的参数文件,show parameter CORRUPT,可以将结果提交给ORACLE SUPPORT SERVICES。如果这样的参数存在,建议重 建数据库。
2、检查操作系统是否支持ORACLE。检查操作系统的错误日志和ORACLE的错误日志。
3、向Oracle Support Services寻求帮助。
补充情况6:
在oracle 9i之后,使用auto的undo,就很少出现ORA-1555错误了,但是也不能完全排除这个错误,比如以下错误:
ORA-01555 caused by SQL statement below (Query Duration=11952 sec, SCN: 0x0842.1a1d737c)
这种情况下,主要是查询的时间超长,超过了undo_retention设置的时间10800秒,从而产生前面的情况1和情况2的错误。
Solution:
主要的解决方法,还是优化查询,包括语句的优化和索引的优化。
附录1:关于事务和数据块的一个说明
一个回滚段能够被多个事务使用;一个extent也可以被多个事务共享;但是一个block只能被一个active的事务使用;
8i中:一个事物只能使用一个回滚段;
9i中:一个事务可以使用多个回滚段;
commit以后rollback segment里还会保存undo_retention 秒.这样以使在执行DML语句以前的开始的大查询在commit以后还是使用修改以前的数据,以保持查询的一致性,否则会产生snapshot too old这个错误
附录2:什么是延迟块清除(delayed block cleanout)
块清除就是删除所修改数据块上与"锁定"有关的信息,即事务信息。Oracle在事务相关的提交列表中,记录下已修改的块列表,每个列表记录20个块,根据需要可能分配有多个这种列表.这种块列表有一个上限,就是缓冲区缓存大小的10%.如果一次修改的块,没有超过了缓冲区缓存大小的10%,并且这些块在内存中,则commit时,会清除块上的事务信息。如果,一次修改的块超过了这个上限值,oracle在commit的时候就不会清除块头的事务信息,直到下次访问这些块时,再清除块中的事务信息,这就是延迟块清除.
当事务提交(假设commit scn为T1),如果该事务是一个大事务,涉及了很多个数据块,超过了data buffer的10%,则提交的时候, oracle并不会再次逐一将commit scn(T1)写到所有相关的data block的ITL上。或者另一种情况是提交的时候,因为某种原因,修改的内容已经被DBWR给写到数据块中,这时,oracle也不会将commit scn(T1)写到该数据块的ITL上。
上面这两种情况的结果是,当之后的事务,比如select(假设scn 为T2)来访问这个数据块的时候,发现在ITL上找不到commit scn,因此无法判断是否需要回退,所以select转而去回滚段中查找相关信息确认是否需要回退。如果在回滚段中找到真实的commit scn(T1),则该事务会将该commit scn(T1)写到数据块的ITL中。之后的查询将可以直接通过ITL确定数据块的状态信息。
如果在回滚段中没有找到真实的commit scn(T1)(由于事务提交频繁,将回滚段覆盖了),则查询该回滚段中的最小scn(T3),若T3T2,则commit是发生在select之前,还是之后,就会报ORA-01555错误。
下面是关于延迟清除的示例
1)初始状态
这个是最开始的情形,在数据块的头部,有一个tx区域,用来保留指向回滚段中的相关活动事务,在回滚段头中则包含有一张存储了所有最近使用了该回滚段的事务信息表。
在下面的例子中,回滚段头的表中包含了两个活动的事务信息(事务槽01和事务槽02),后面的事务槽则都是已经commit的,可以再次使用的。
Data Block 500 Rollback Segment Header 5
+----+--------------+ +----------------------+---------+
| tx | None | | transaction entry 01 |ACTIVE |
+----+--------------+ | transaction entry 02 |ACTIVE |
| row 1 | | transaction entry 03 |COMMITTED|
| row 2 | | transaction entry 04 |COMMITTED|
| ... .. | | ... ... .. | ... |
| row n | | transaction entry nn |COMMITTED|
+-------------------+ +--------------------------------+
2)更新了row 2
下面更新数据块中的row 2。此时,数据块头部tx区域已更新指向回滚段中的事务槽3(5.3)的位置,事务槽03已经被标记为active。
Data Block 500 Rollback Segment Header 5
+----+--------------+ +----------------------+---------+
| tx |5.3uncommitted|-+ | transaction entry 01 |ACTIVE |
+----+--------------+ | | transaction entry 02 |ACTIVE |
| row 1 | +--> | transaction entry 03 |ACTIVE |
| row 2 *changed* | | transaction entry 04 |COMMITTED|
| ... .. | | ... ... .. | ... |
| row n | | transaction entry nn |COMMITTED|
+------------------+ +--------------------------------+
3)commit
之后,用户提交了该更新操作。提交操作更新的回滚段头部的事务列表信息,将事务槽03从active状态更新为commited状态。但对于数据块却未作任何操作。
Data Block 500 Rollback Segment Header 5
+----+--------------+ +----------------------+---------+
| tx |5.3uncommitted|--+ | transaction entry 01 |ACTIVE |
+----+--------------+ | | transaction entry 02 |ACTIVE |
| row 1 | +---> | transaction entry 03 |COMMITTED|
| row 2 *changed* | | transaction entry 04 |COMMITTED|
| ... .. | | ... ... .. | ... |
| row n | | transaction entry nn |COMMITTED|
+------------------+ +--------------------------------+
4)commit之后的其他事务访问
过了一段时间之后,另外一个用户(或者同一个用户)再次访问该数据块,我们其实可以看到,在数据块的头部信息中,并没有关于row 2已经提交的信息。
oracle利用数据块的头部信息找到与row 2更新事务相关的回滚段的段头的列表信息。在这里发现,该事务已经提交,则更新数据块的信息 ,表明row 2已经commited。
Data Block 500 Rollback Segment Header 5
+----+--------------+ +----------------------+---------+
| tx | None | | transaction entry 01 |ACTIVE |
+----+--------------+ | transaction entry 02 |ACTIVE |
| row 1 | | transaction entry 03 |COMMITTED|
| row 2 | | transaction entry 04 |COMMITTED|
| ... .. | | ... ... .. | ... |
| row n | | transaction entry nn |COMMITTED|
+------------------+ +--------------------------------+
附录3:什么是ITL事务槽?--Interested Transaction List(ITL)
ITL内容包括:
xid---Transaction ID
Uba---Undo Block Address
Lck---Lock Status
Oracle在读一个block时,如果ITL事务槽存在活动事务,那么Oracle会根据相应的xid找到相应的回滚段以判断事务状态.
|
|