(由于本论坛不支持前导空格, 所有的程序语句都用__来表示空格, 以便阅读)
本专题分为以下几个部分:
* 回滚段的作用
* 回滚段的类型
* 回滚段的数量、大小及存储参数
* 回滚段的维护及查询
* 有关回滚段的常见错误及解决方法
1. 回滚段的作用
回滚段用于对数据库修改时, 保存原有的数据, 以便稍后可以通过使用ROLLBACK来恢复到修改前的数据; 另外, 回滚段可以为数据库中的所有进程提供读一致性. 因此, 回滚段设置的合理与否, 直接影响到数据库的性能, 在更新密集的OLTP应用中,更是如此.
2. 回滚段的类型
回滚段可分为系统回滚段和非系统回滚段, 其中非系统回滚段又分为PUBLIC回滚段和PRIVATE回滚段.
系统回滚段用于处理涉及系统的CATALOG的事物(比如大多数的DDL), 它位于SYSTEM表空间, 由于只有SYSTEM表空间可以随时保持可用, 因此, 不要把SYSTEM回滚段放在其他的表空间中.
*** 原则1: 系统回滚段应放在SYSTEM表空间中, 并且应该永远保持ONLINE状态.
PUBLIC回滚段对于数据库的所有实例(INSTANCE)都是可用的, 除非将其显式设置为OFFLINE.
PRIVATE回滚段是指对于数据库的某个实例是私有的, 为了使用PRIVATE回滚段, 某个实例应当在其INITsid.ORA的ROLLBACK_SEGMENTS中标明所有要使用的PRIVATE回滚段, 或通过使用ALTER ROLLBACK SEGMENT XXX ONLINE来使用某一个回滚段.
*** 建议1: 在单实例系统中,建议将所有回滚段设为PUBLIC.
*** 建议2: 在多实例系统中(如OPS), 建议将每个实例的PRIVATE回滚段放置到访问比较快的本地设备上.
3. 回滚段的数量、大小及存储参数
精确的回滚段的数量及大小的计算涉及很多方面: 应用的类型(OLTP/OLAP/BATCH), 同时进行的事物的数量, DML语句的类型, 每个事物处理的数据量等等. 精确的计算, 限于篇幅, 不在此提及, 朋友们可参考相关文档(参考文献4), 在此, 只提供几个原则及建议.
*** 原则2: OLTP系统应使用小但较多的回滚段, OLAP系统/批处理系统应使用少量的大回滚段
*** 建议3: OLTP/OLAP混合型系统中, 应专门设置一个或几个大的回滚段, 平时设置为OFFLINE, 使用时通过使用SET TRANSACTION USE ROLLBACK SEGMENT XXX来使用它. 这些回滚段应使用OPTIMAL参数,以便在不使用时,可以SHRINK到一个较小的尺寸。
*** 建议4: 在很难计算准确的数量、大小时,可用"偏大不偏小"的原则。
*** 原则3: 所有的回滚段的INITIAL/NEXT参数应设为相同, 只有建议3中提到的大回滚段例外.
*** 原则4: 不要将回滚段的MAXEXTENTS设为UNLIMITED, 回滚段所在表空间也不要设为AUTOEXTEND
方式, 否则将会使得由于某个不正常的事务导致整个数据库处于失控状态.
4. 回滚段的维护及查询
(1) 创建回滚段
__CREATE ROLLBACK SEGMENT RB01
__TABLESPACE RBS1
__STORAGE (
____INITIAL 100K
____NEXT 100K
____MINEXTENTS 20
____MAXEXTENTS 100
____OPTIMAL 2000K );
2) 更改ONLINE/OFFLINE状态
__ALTER ROLLBACK SEGMENT RB01 ONLINE;
__ALTER ROLLBACK SEGMENT RB01 OFFLINE;
(3) 更改OPTIMAL参数
__ALTER ROLLBACK SEGMENT RB01
__STORAGE ( MAXEXTENTS 200
____OPTIMAL 2048K );
(4) 缩小回滚段
__ALTER ROLLBACK SEGMENT RB01 SHRINK;
(有OPTIMAL参数时, 缩小到OPTIMAL值; 没有OPTIMAL参数时, 缩小到MINEXTENTS所对应的尺寸)
__ALTER ROLLBACK SEGMENT RB01 SHRINK TO 2048K;
(5) 修改INITIAL/NEXT参数
*** 建议5: 根据原则3, 修改NEXT时, 总应该同时修改INITIAL.
INITIAL参数无法直接修改, 只能先DROP, 然后再CREATE.
__DROP ROLLBACK SEGMENT RB01;
__CREATE ROLLBACK SEGMENT RB01
__TABLESPACE RBS1
__STORAGE ( INITIAL 100K
____NEXT 100K
____MINEXTENTS 20
____MAXEXTENTS 121
____OPTIMAL 2000K )
(6) 在事务中使用特定的回滚段
__SET TRANSACTION USE ROLLBACK SEGMENT RB_LARGE1;
(7) 常用的有关回滚段的系统数据字典
DBA_ROLLBACK_SEGS (相关表:DBA_SEGMENTS)
V$ROLLNAME
V$ROLLSTAT
V$TRANSACTION (相关表: V$session)
5. 有关回滚段的常见错误及解决方法
(1) 回滚段空间不够
ORA-01562 - failed to extend rollback segment number string
回滚段空间不够的原因一般有以下几种情况:
A. 回滚段所在表空间剩余的空闲空间太小, 无法分配下一个EXTENT.
B. 回滚段扩展次数已经达到MAXEXTENTS限制
解决方法:
A. 扩大回滚段所在表空间
B. 设置较大的MAXEXTENTS参数
C. 为回滚段设置OPTIMAL参数
D. 用较大的EXTENT参数重新创建回滚段
C. 将导致ORA-1562错误的DML语句改为分段执行:
例如: 原来的语句为
____DELETE FROM HUGETABLE WHERE condition;
可用如下语句代替:
____BEGIN
________LOOP
____________DELETE FROM HUGETABLE
____________WHERE condition
____________AND ROWNUM<10000;
____________EXIT WHEN SQL%NOTFOUND;
____________COMMIT;
________END LOOP;
____END;
(2) ORA-01552 cannot use system rollback segment for non-system tablespace
'string'
原因: 没有可用的非系统回滚段. 分为以下情形:
A. 除了系统回滚段, 未创建其它回滚段
B. 只创建了PRIVATE回滚段, 但INITsid.ORA的ROLLBACK_SEGMENTS中未列出这些回滚段
C. 创建了PUBLIC回滚段, 但这些回滚段都处于OFFLINE状态
解决方法: 根据以上原因相应解决即可
(3) 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 logg