OracleORA-01555快照过旧的错误(转载)
第⼀篇⽂章:
⾸先了解在什么情况下会产⽣ORA-01555错误:
假设有⼀张6000万⾏数据的testdb表,预计testdb全表扫描1次需要2个⼩时,参考过程如下:
1、在1点钟,⽤户A发出了select * from testdb;此时不管将来testdb怎么变化,正确的结果应该是⽤户A会看到在1点钟这个时刻的内容。
2、在1点30分,⽤户B执⾏了update命令,更新了testdb表中的第4100万⾏的这条记录,这时,⽤户A的全表扫描还没有到达第4100万条。毫⽆疑问,这个时候,第4100万⾏的这条记录是被写⼊了回滚段,假设是回滚段UNDOTS1,如果⽤户A的全表扫描到达了第4100万⾏,是应该会正确的从回滚段UNDOTS1中读取出1点钟时刻的内容的。
3、这时,⽤户B将他刚才做的操作提交了,但是这时,系统仍然可以给⽤户A提供正确的数据,因为那第4100万⾏记录的内容仍然还在回滚段UNDOTS1⾥,系统可以根据SCN到回滚段⾥到正确的数据,但要注意到,这时记录在UNDOTS1⾥的第4100万⾏记录已经发⽣了重⼤的改变:就是第4100万⾏在回滚段UNDOTS1⾥的数据有可能随时被覆盖掉,因为这条记录已经被提交了!
4、由于⽤户A的查询时间漫长,⽽业务在⼀直不断的进⾏,UNDOTS1回滚段在被多个不同的transaction使⽤着,这个回滚段⾥的extent 循环到了第4100万⾏数据所在的extent,由于这条记录已经被标记提交了,所以这个extent是可以被其他transaction覆盖掉的!
5、到了1点45分,⽤户A的查询终于到了第4100万⾏,⽽这时已经出现了第4条说的情况,需要到回滚段UNDOTS1去数据,但是已经被覆盖掉了,这时就出现了ORA-01555错误。
原因分析:"报表"程序执⾏时间漫长,在程序查询的过程中其他⽤户对"报表"进⾏了更新,被更新的数据写⼊了回滚段,当程序到回滚段数据时,发现数据已经被覆盖掉,于是就出现了ORA-01555错误。另外"报表"程序执⾏效率不⾼也会造成ORA-01555错误。
解决办法:
1、扩⼤回滚段,因为回滚段是循环使⽤的,如果回滚段⾜够⼤,那么那些被提交的数据就能保存⾜够长的时间,使那些⼤事务完成⼀致性读取。之前EBS系统UNDO表空间为9GB,⽬前为10GB。见下图:
2、增加undo_retention时间,因为UNDO回滚段是循环使⽤,⾥⾯的数据可能随时被循环覆盖掉,如果设置undo_retention时间更长,那么在retention规定的时间内,任何其他事务都不能覆盖这些数据。⽬前EBS系统undo_retention为10800秒(3个⼩时)。见下图:
3、最重要的⼀点就是优化程序相关查询语句,减少查询语句的⼀致性读,降低读取不到回滚段数据的风险。所有的出错信息都会纪录到数据库⽇志alert_PROD.log⽂件中,下图红线部分是⼀条SQL查询词句,ORA-01555很有可能是这条语句造成,把这条语句提供给开发⼈员来分析和优化程序代码。
ORA-01555 原因与解决:
前⾯提到了ORA-01555错误,那么现在来看⼀下ORA-01555错误是怎样产⽣的。由于回滚段是循环使⽤的,当事务提交以后,该事务占⽤的回滚段事务会被标记为⾮活动,回滚段空间可以被覆盖重⽤。那么⼀个问题就出现了,如果⼀个查询需要使⽤被覆盖的回滚段构造前镜像实现⼀致性读,那么此时就会出现著名的ORA-01555错误。
ORA-01555错误的另外⼀个原因是因为延迟块清除(Delayed Block Cleanout)。当⼀个查询触发延迟块清除时,Oracle需要去查询回滚段获得该事务的提交SCN,如果事务的前镜像信息已经被覆盖,并且查询SCN也⼩于回滚段中记录的最⼩提交SCN,那么Oracle将⽆从判断查询SCN和事务提交SCN的⼤⼩,此时出现延迟块清除导致的ORA-01555错误。
另外⼀种导致ORA-01555错误的情况出现在使⽤sqlldr直接⽅式加载(direct=true)数据时。当通过sqlldr direct=true ⽅式加载数据时,由于不产⽣重做和回滚信息,Oracle直接指定Cached Commit SCN 给加载数据,在访问这些数据时,有时会产⽣ORA-01555错误。淮安邮编
看下图的描述:假定在时间T⽤户A发出⼀条更新语句,更新SCOTT⽤户的SAL;⽤户B在Ty时间发出查询语句,查询SCOTT⽤户的SAL;⽤户A的更新在Tx时间提交,提交可能为快速提交块清除,也可能是延迟块清除;⽤户B的查询在Tz时间输出。
来看⼀下数据库在不同情况下的内部处理:
·如果 Ty < T < Tz < Tx ,那么查询需要构造⼀致性读,由于事务尚未提交,可以通过回滚段构造前镜像,完成⼀致性读取。
·如果 Ty < T < Tx < Tz ,由于Ty查询时间⼩于T事务更新时间,那么数据库需要构造⼀致性读取,⽽Tz查询完成时间⼤于Tx提交时间,那么前镜像就有可能被覆盖,不可获取。
如果Tx的提交⽅式为Fast Block Cleanout,那么回滚段信息不可⽤时就会出现⼀致性读ORA-01555错误。
如果Tx的提交⽅式为Delayed Block Cleanout,那么回滚段信息不可⽤时Oracle将⽆法判断Ty和Tx的时间先后关系。如果 Ty > Tx ,那么Oracle可以正常进⾏块清除,并将块清除后的数据返回给⽤户B;如果 Ty < T ,那么Oracle需要继续构造⼀致性读返回给⽤户B;Oracle⽆法判断这两种情况,就会出现延迟块清除ORA-01555错误。
ORA-01555的直观解释是“snapshot too old”,也就是快照太旧,其根本含义就是查询需要的前镜像过于“久远”,已经⽆法到了。可以想象,如果⼀个历时数个⼩时或⼗⼏个⼩时的查询,如果最后遭遇ORA-01555错误⽽失败,会是多么令⼈沮丧的⼀件事。⼀直以来,ORA-01555都是ORACLE最为头痛的问题之⼀。
在Oracle 9i的⽂档中这样描述ORA-01555错误:
01555, 00000, "snapshot too old: rollback segment number %s with name \"%s\" too small"
// *Cause: rollback records needed by a reader for consistent read are
// overwritten by other writers
// *Action: If in Automatic Undo Management mode, increase undo_retention
// setting. Otherwise, use larger rollback segments
可以看到,在Oracle 9i⾃动管理UNDO表空间模式下,UNDO_RETENTION参数的引⼊正是为了减少ORA-01555错误的出现。这个参数设置当事务提交之后(回滚段变得⾮活跃),回滚段中的前镜像数据在被覆盖前保留的时间,该参数以秒为单位,9iR1初始值为900秒,在Oracle 9iR2增加为10800秒。
显然该参数设置的越⾼就越能减少ORA-01555错误的出现,但是保留时间和存储空间是紧密相关的,如果UNDO表空间的存储空间有限,那么Oracle就会选择回收已提交事务占⽤的空间,置UNDO_RETENTION参数于不顾。
在Oracle 9i的AUM模式下,UNDO_RETENTION实际上是⼀个⾮担保(NO Guaranteed)限制。也就是说,如果有其他事务需要回滚空间,⽽空间出现不⾜时,这些信息仍然会被覆盖;从Oracle 10g开始,Oracle对于UNDO增加了Guarantee控制,也就是说,可以指定UNDO表空间必须满⾜UNDO_RETENTION的限制。当UNDO表空间设置为Guarantee,那么提交事务的回滚空间必须被保留⾜够的时间,如果UNDO表空间的空间不⾜,那么新的事务会因空间不⾜⽽失败,⽽不是选择之前的覆盖。
从各个不同版本回滚段的管理变迁,我们可以看出Oracle⼀直在进步。
Oracle提供了⼀个内部事件(10203事件)可以⽤来跟踪数据库的块清除操作,10203事件可以通过以下命令设置,设置后需要重新启动数据库该参数⽅能⽣效:
alter system set event="10203 trace name context forever" scope=spfile;
需要注意的是,可能存在另外⼀种情况,就是当执⾏延迟块清除时,回滚段或原回滚表空间已经被删除,此时Oracle仍然可以通过字典表UNDO$来获得SCN信息,执⾏块清除。
关于Oracle的提交处理及块清除机制是⼀个极其复杂的过程,本⽂对这部分内容进⾏了适当简化说明,旨在使⼤家能够对Oracle的回滚机制、块清除机制有所了解。
-b型血人的性格特点
The End -
第⼆篇⽂章:
写了段java操作数据库的代码
Jav a代码
1. String getIPList="select t.dns_ip from t_dnscachetotal t where t.locid=0";
2. String getLocid="select t
3.locid from (select max(t2.ipstart) ipstart,max(t2.ipend) ipend from t_GGMAP_IP t2 where t2.ipstart<=query_ip(?)) t1,t_GGMAP_IP t3 wh
3. String updateDnsCacheTotal="update t_dnscachetotal t set t.locid=? where t.dns_ip=?";
4.
5. stmt=con.prepareStatement(getIPList);
6. uteQuery();
7. String ip;
8.
9. ResultSet rs2;
10. int countupdate=0;
11. ()){
12. String(1);
13. pstmt2 = con.prepareStatement(getLocid);
14. pstmt2.setString(1, ip);
15. pstmt2.setString(2, ip);
16. uteQuery();
17. int locid=-1;
炒糖怎么炒
18. ()){
海南游记19. Int(1);
20. }电导率仪的使用>中原节日是什么意思
21. pstmt2.close();
22. rs2.close();
23. countupdate++;
24. pstmt2=con.prepareStatement(updateDnsCacheTotal);
25. pstmt2.setInt(1, locid);
26. pstmt2.setString(2, ip);
27. uteUpdate();
28. pstmt2.close();
29.
30. }
31. pstmt.close();
32. rs.close();
⼤体就这样。。我删了⼀部分代码。
核⼼的问题就在于
()){
}
因为rs.next实际上是对oracle某表持续的查询,⽽在循环中⼜在不断地update这个表,从⽽导致了这个1555错误,
ora 1555别⼈的例⼦ 写道
⾸先了解Oracle在什么情况下会产⽣ORA-01555错误:
假设有⼀张6000万⾏数据的testdb表,预计testdb全表扫描1次需要2个⼩时,参考过程如下:
1、在1点钟,⽤户A发出了select * from testdb;此时不管将来testdb怎么变化,正确的结果应该是⽤户A会看到在1点钟这个时刻的内容。
2、在1点30分,⽤户B执⾏了update命令,更新了testdb表中的第4100万⾏的这条记录,这时,⽤户A的全表扫描还没有到达第4100万条。毫⽆疑问,这个时候,第4100万⾏的这条记录是被写⼊了回滚段,假设是回滚段UNDOTS1,如果⽤户A的全表扫描到达了第4100万⾏,是应该会正确的从回滚段
UNDOTS1中读取出1点钟时刻的内容的。
3、这时,⽤户B将他刚才做的操作提交了,但是这时,系统仍然可以给⽤户A提供正确的数据,因为那第4100万⾏记录的内容仍然还在回滚段UNDOTS1⾥,系统可以根据SCN到回滚段⾥到正确的数据,但要注意到,这时记录在UNDOTS1⾥的第4100万⾏记录已经发⽣了重⼤的改变:就是第4100万⾏在回滚段UNDOTS1⾥的数据有可能随时被覆盖掉,因为这条记录已经被提交了!
4、由于⽤户A的查询时间漫长,⽽业务在⼀直不断的进⾏,UNDOTS1回滚段在被多个不同的transaction使⽤着,这个回滚段⾥的extent循环到了第4100万⾏数据所在的extent,由于这条记录已
经被标记提交了,所以这个extent是可以被其他transaction覆盖掉的!
5、到了1点45分,⽤户A的查询终于到了第4100万⾏,⽽这时已经出现了第4条说的情况,需要到回滚段UNDOTS1去数据,但是已经被覆盖掉了,这时就出现了ORA-01555错误。
明显我是犯了同样的错误。⽽解决办法⼤致有三个
⾸先:
SQL> show parameter undo
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
undo_management string AUTO
undo_retention integer 900
undo_tablespace string UNDOTBS1
可以看到我们是⾃动管理undo的,(11g),另外undo_retention时间是900s,这个是可以考虑放⼤的,
写道
关于初始化参数UNDO_RETENTION的设置,严格说起来也是与UNDO表空间有关系,但是思量再三,我觉着还是有必要单拎出来详细介绍。
该参数⽤来指定UNDO段中数据保存的最短时间,以秒为单位,是⼀个动态参数,完全可以在实例运⾏时随时修改,通常默认是900秒,也就是15分钟。
⾸先要注意,UNDO_RETENTION只是指定UNDO段中数据的过期时间,并不是说,UNDO段中的数据⼀定会在UNDO表空间中保存15分钟。如⼀个新事务开始的时候,如果此时UNDO表空间已经被写满,则新事务的数据会⾃动覆盖已提交事务的数据,⽽不管这些数据是否已过期,因此呢,这就⼜关联回了第⼀点,当你创建⼀个⾃动管理的UNDO表空间时,还要注意其空间⼤⼩,要尽可能保证UNDO表空间有⾜够的存储空间。
同时还要注意,也并不是说,UNDO_RETENTION中指定的时间⼀过,已经提交事务中的数据就⽴刻⽆法访问,当超出UNDO_RETENTION参数指定的时间后,这部分数据占⽤的空间将会被标识为可重⽤,不过只要不被别的事务触发的数据覆盖,它会仍然存在,并可以随时被Flashback特性引⽤。如果你的UNDO表空间⾜够⼤,⽽数据库⼜不是那么繁忙,那么其实UNDO_RETENTION参数的值并不会影响到你,哪怕你设置成1(这么说好像绝对了点,⼤家⼀定要注意理解,别钻⽜⾓尖),只要没
有事务去覆盖UNDO数据,这部分数据就会持续有效。因此呢,再次重复那句话,要注意UNDO表空间的⼤⼩,保证其有⾜够的存储空间。
最后,只有在⼀种情况下,UNDO表空间能够确保UNDO中的数据在UNDO_RETENTION指定时间过期前⼀定有效,就是为UNDO表空间指定RETENTION GUARANTEE,指定之后,不会覆盖UNDO表空间中未过期的UNDO数据,例如:
SQL> ALTER TABLESPACE UNDOTBS1 RETENTION GUARANTEE; 如果想禁⽌UNDO表空间RETENTION GUARANTEE,例如:
SQL> ALTER TABLESPACE UNDOTBS1 RETENTION NOGUARANTEE; 转了⼀圈,问题⼜回来了,既然它看起来有⽤⼜像没有⽤,为什么还要设置它呢?嘿嘿,就我理解,其存在的真实⽤途,就是提醒你UNDO表空间很重要,给它指定分配⼀个合适的⼤⼩更重要哟。
但是从这篇⽂章中明显发现,undo_retention参数意义很⼩,⽽且默认的TABLESPACE UNDOTBS1 ⼀般都是NOGUARANTEE,这个是⼀个考虑解决1555错误的⽅法,但是我个⼈觉得⼤部分情况绝不是因为undo_retention过⼩引起的,应该都是因为UNDOTBS1 占满了的缘故,所以
1、扩⼤回滚段
因为回滚段是循环使⽤的,如果回滚段⾜够⼤,那么那些被提交的数据信息就能保存⾜够长的时间是那些⼤事务完成⼀致性读取。
2、增加undo_retention时间
在undo_retention规定的时间内,任何其他事务都不能覆盖这些数据。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论