ORA-01555: 快照过旧: 回退段号 4 在名称为 "_SYSSMU4$" 过小
我用java做的开发,表中数据比较多,几万条吧,当执行到一定程度时候,就出此错误。我已经知道要增加回退段,但是我不知道具体该怎么做,如何将此大事务指定到此回退段?请具体给一个例子好吗。要一步一步的写清楚,我知道写一点java程序,对此一点也不清楚,只能照葫芦画瓢。谢谢了 问题点数:100、回复次数:21Top
1 楼tomhuang(春城)回复于 2005-03-07 13:54:44 得分 0
回滚段太小,你的数据库版本是??Top
2 楼tomhuang(春城)回复于 2005-03-07 13:55:45 得分 0
好像是set resegment...Top
3 楼kerisyml(魂之利刃)回复于 2005-03-07 14:09:14 得分 0
ORA_01555 snapshot too old: rollback segment number string with name "string" too small
原因可分为以下情形:
A. 回滚段太少/太小
数据库中有太多的事务修改数据并提交, 就会发生已提交事务曾使用的空间被重用, 从而造成一个延
续时间长的查询所请求的数据已经不在回滚段中.
解决方法: 创建更多的回滚段, 为回滚段设置较大的EXTENT以及较大的MINEXTENTS
B. 回滚段被破坏
由于回滚段被破坏, 造成事务无法将修改前的内容(read-consistent snapshot) 放入回滚段, 也会产生ORA-01555错误.
解决方法: 将被破坏的回滚段OFFLINE, 删除重建.
C. FETCH ACROSS COMMIT
当一个进程打开一个CURSOR, 然后循环执行FETCH, UPDATE, COMMIT, 如果更新的表与FETCH的是同一个表, 就很可能发生ORA-01555错误.
解决方法:
a. 使用大的回滚段
b. 减少提交频率(可参见本论坛"如何避免一个PROCEDURE被重复调用"一贴中, 无名朋友的回帖)
以上两种方法只能减少该错误发生的可能, 不能完全避免. 如果要完全避免, 须从执行方法着手, 可以用以下两种方法:
c. 建立一个临时表, 存放要更新的表的查询列(如主键及相关的条件列), 从临时表FETCH, 更新原来的表.
d. 捕获ORA-01555错误, 关闭并重新打开CURSOR, 继续执行循环:
示例(示例程序的思路来源自ORACLE的UTLIP.SQL, 有兴趣的朋友可直接阅读该程序, 位置在RDBMS\ADMIN下, 程序很短, 容易读):
____DECLARE
____LAST_PK NUMBER := 0;
____V_THEROWID ROWID;
____CURSOR C1 IS
________SELECT ROWID, PK, …
________FROM SMPLE
________WHERE PK > LAST_PK
________AND othercondition
________ORDER BY PK;
____BEGIN
________OPEN c_SOURCE;
________LOOP
____________BEGIN
________________FETCH C1 INTO v_THEROWID, v_PK;
________________EXIT WHEN C1%NOTFOUND;
____________EXCEPTION WHEN OTHERS THEN
________________IF SQLCODE = -1555 THEN -- snapshot too old, re-execute fetch query
____________________CLOSE C1;
____________________OPEN c_SOURCE;
____________________GOTO NEXTLOOP01555;
________________ELSE
____________________RAISE;
________________END IF;
____________END;
____________LAST_PK := PK;
……… … PROCESS, UPDATE AND COMMIT
____________<>
____________NULL;
________END LOOP;
________CLOSE C1;
____END;
D. 其它原因:
* Delayed logging block cleanout是ORACLE用来提高写性能的一种机制: 当修改操作(INSERT/UPDATE/DELETE)发生时, ORACLE将原有的内容写入回滚段, 更新每个数据块的头部使其指向相应的回滚段, 当该操作被COMMIT时, ORACLE并不再重新访问一遍所有的数据块来确认所有的修改, 而只是更新位于回滚段头部的事务槽来指明该事务已被COMMIT, 这使得写操作可以很快结束从而提高了性能接下来的任何访问该操作所修改的数据的操作会使先前的写操作真正生效, 从而访问到新的值. Delayed logging block cleanout 虽然提高了性能, 但却可能导致ORA-01555. 这种情况下, 在OPEN/FETCH前对该表做全表扫描(保证所有的修改被确认)会有所帮助.
* 不适当的OPTIMAL参数: 太小的OPTIMAL参数会使回滚段很快被SHRINK, 造成后续读取操作访问时, 先前的内容已丢失. 仔细设计OPTIMAL参数, 不要让回滚段过于频繁的EXTEND/SHRINK有助于问题的解决.
* DB BLOCK BUFFER太小: 如果读一致性所请求的块的先前内容在缓冲区中, 那么就不用去访问回滚段. 而如果缓冲区太小, 使得先前版本的内容在CACHE中的可能性变小, 从而必须频繁的访问回滚段来获取先前的内容, 这将大大增大ORA-01555发生的可能.Top
4 楼NinGoo(http://www.NinGoo.net)回复于 2005-03-07 14:09:18 得分 0
参考:
ORA-01555:snapshot too old (rollback segment too small)
---- 这说明 oracle 给此事务随机分配的回滚段太小了 , 这时可以为它指定一个足够大的回滚段 , 以确保这个事务的成功执行 . 例如
set transaction use rollback segment roll_abc;
delete from table_name where ...
commit;
---- 回滚段 roll_abc 被指定给这个 delete 事务 ,commit 命令则在事务结束之后取消了回滚段的指定 .
Top
5 楼tonytyk()回复于 2005-03-07 14:13:54 得分 0
楼上的,你这个我看过了,但是我不知道如何在java程序中指定。我是先选择数据,然后循环对每一行进行操作。在java社区提了这个问题,一个回复都没有。Top
6 楼kerisyml(魂之利刃)回复于 2005-03-07 14:16:05 得分 20
ORA_01555 snapshot too old: rollback segment number string with name "string" too small
原因可分为以下情形:
A. 回滚段太少/太小
数据库中有太多的事务修改数据并提交, 就会发生已提交事务曾使用的空间被重用, 从而造成一个延
续时间长的查询所请求的数据已经不在回滚段中.
解决方法: 创建更多的回滚段, 为回滚段设置较大的EXTENT以及较大的MINEXTENTS
B. 回滚段被破坏
由于回滚段被破坏, 造成事务无法将修改前的内容(read-consistent snapshot) 放入回滚段, 也会产生ORA-01555错误.
解决方法: 将被破坏的回滚段OFFLINE, 删除重建.
C. FETCH ACROSS COMMIT
当一个进程打开一个CURSOR, 然后循环执行FETCH, UPDATE, COMMIT, 如果更新的表与FETCH的是同一个表, 就很可能发生ORA-01555错误.
解决方法:
a. 使用大的回滚段
b. 减少提交频率(可参见本论坛"如何避免一个PROCEDURE被重复调用"一贴中, 无名朋友的回帖)
以上两种方法只能减少该错误发生的可能, 不能完全避免. 如果要完全避免, 须从执行方法着手, 可以用以下两种方法:
c. 建立一个临时表, 存放要更新的表的查询列(如主键及相关的条件列), 从临时表FETCH, 更新原来的表.
d. 捕获ORA-01555错误, 关闭并重新打开CURSOR, 继续执行循环:
示例(示例程序的思路来源自ORACLE的UTLIP.SQL, 有兴趣的朋友可直接阅读该程序, 位置在RDBMS\ADMIN下, 程序很短, 容易读):
____DECLARE
____LAST_PK NUMBER := 0;
____V_THEROWID ROWID;
____CURSOR C1 IS
________SELECT ROWID, PK, …
________FROM SMPLE
________WHERE PK > LAST_PK
________AND othercondition
________ORDER BY PK;
____BEGIN
________OPEN c_SOURCE;
________LOOP
____________BEGIN
________________FETCH C1 INTO v_THEROWID, v_PK;
________________EXIT WHEN C1%NOTFOUND;
____________EXCEPTION WHEN OTHERS THEN
________________IF SQLCODE = -1555 THEN -- snapshot too old, re-execute fetch query
____________________CLOSE C1;
____________________OPEN c_SOURCE;
____________________GOTO NEXTLOOP01555;
________________ELSE
____________________RAISE;
________________END IF;
____________END;
____________LAST_PK := PK;
……… … PROCESS, UPDATE AND COMMIT
____________<>
____________NULL;
________END LOOP;
________CLOSE C1;
____END;
D. 其它原因:
* Delayed logging block cleanout是ORACLE用来提高写性能的一种机制: 当修改操作(INSERT/UPDATE/DELETE)发生时, ORACLE将原有的内容写入回滚段, 更新每个数据块的头部使其指向相应的回滚段, 当该操作被COMMIT时, ORACLE并不再重新访问一遍所有的数据块来确认所有的修改, 而只是更新位于回滚段头部的事务槽来指明该事务已被COMMIT, 这使得写操作可以很快结束从而提高了性能接下来的任何访问该操作所修改的数据的操作会使先前的写操作真正生效, 从而访问到新的值. Delayed logging block cleanout 虽然提高了性能, 但却可能导致ORA-01555. 这种情况下, 在OPEN/FETCH前对该表做全表扫描(保证所有的修改被确认)会有所帮助.
* 不适当的OPTIMAL参数: 太小的OPTIMAL参数会使回滚段很快被SHRINK, 造成后续读取操作访问时, 先前的内容已丢失. 仔细设计OPTIMAL参数, 不要让回滚段过于频繁的EXTEND/SHRINK有助于问题的解决.
* DB BLOCK BUFFER太小: 如果读一致性所请求的块的先前内容在缓冲区中, 那么就不用去访问回滚段. 而如果缓冲区太小, 使得先前版本的内容在CACHE中的可能性变小, 从而必须频繁的访问回滚段来获取先前的内容, 这将大大增大ORA-01555发生的可能.Top
7 楼libin_ftsafe(子陌红尘:TS for Banking Card)回复于 2005-03-07 14:32:38 得分 0
增大回滚段的大小。Top
8 楼tonytyk()回复于 2005-03-07 15:07:04 得分 0
我的程序大概是这样
PreparedStatement pStmt = conn.prepareStatement("select * from table");
ResultSet rst = pStmt.executeQuery();
while(rst.next())
{
do something;
}Top
9 楼tonytyk()回复于 2005-03-07 15:10:48 得分 0
我的程序大概是这样
PreparedStatement pStmt = conn.prepareStatement( "select * from table ");
ResultSet rst = pStmt.executeQuery();
while(rst.next())
{
do something;
...
在这里更新相应行;
}
在程序中可以指定到某一回退段吗
Top
10 楼nebulaly(极高明而道中庸)回复于 2005-03-07 17:42:55 得分 0
增加回滚段大小无用,增加回滚段个数可能有用
在循环中commit的典型问题Top
11 楼tonytyk()回复于 2005-03-08 08:47:13 得分 0
楼上能说的具体点吗,拜托了Top
12 楼yujiabian(流氓兔子雨)回复于 2005-03-08 09:43:52 得分 0
ORA-01555 snapshot too old: rollback segment number string with name "string" too small
Cause Rollback records needed by a reader for consistent read are overwritten by other writers.
Action Use larger rollback segments.
Top
13 楼nebulaly(极高明而道中庸)回复于 2005-03-08 13:38:14 得分 10
这个问题涉及ORACLE的事务处理实现方式
楼主可以简单的理解为:在执行长查询的同时频繁commit,回滚段被重用,
查询读不到其中保存的数据前象,就会出现01555错误Top
14 楼nebulaly(极高明而道中庸)回复于 2005-03-08 13:41:24 得分 10
由于commit之后,事务结束
下一条DML会选择一个空闲的回滚段使用,
由于频繁commit,事务的数据量很小
所以增加回滚段个数有用,增加会滚段尺寸无用Top
15 楼tonytyk()回复于 2005-03-09 09:38:44 得分 0
各位,我不是数据库管理员,对Oracle也不是很熟悉。我用的Oracle版本是9i,我建立一个新回滚段时提示我自动提交模式下的非法操作,我改了init.ora文件将提交模式设置为手动了,但是只能在系统空间中建回滚段,我是不是应该在表所在的空间建立回滚段才有效呢。我是不是应该把提交模式改回到自动模式?增加回滚段个数之后系统就会自动调用吗,还是要我自己指定呢?我是在程序中写的SQL语句,而且不能把这个事务写成过程,这里有哪位好心的高人多多指点一下。Top
16 楼nebulaly(极高明而道中庸)回复于 2005-03-09 13:01:59 得分 0
解决办法很简单:不要在循环中commit,也就是采用手动提交模式,在循环外面commit
btw:为回滚段建立单独的表空间,否则存在很大隐患Top
17 楼hippie1024(努力必有痕迹)回复于 2005-03-09 13:46:01 得分 30
看你回滚段名字就能看出是9i的自动回滚段空间管理了。
1.你可以把参数undo_tablespace中设置的undo表空间加大
2.同时如果你的查询时间超过undo_retention的设置也会发生,可以加大它
3.9i前面的版本有些bug也会导致ORA-01555,考虑升级到9206Top
18 楼tonytyk()回复于 2005-03-10 09:04:39 得分 0
nebulaly(nebulaly):在循环外面提交?如果我提交的数据有很多的话这样做可以吗?
这么多种说法我该听谁的?Top
19 楼epicurus()回复于 2005-03-10 11:25:56 得分 30
一是建个较大回滚段,一定要重建,要用更大的存储参数,仅修改MAXEXTENTS是没用的
二是这个回滚段要位于非系统的表空间上
三是set resegment为该事务指定用这个较大的回滚段Top
20 楼nebulaly(极高明而道中庸)回复于 2005-03-10 12:29:37 得分 0
如果数据量大,可以手动指定回滚段
但一般没有必要Top
21 楼tonytyk()回复于 2005-03-11 09:12:30 得分 0
失望Top




