(由于本論壇不支持前導空格, 所有的程序語句都用__來表示空格, 以便閱讀)
本專題分為以下幾個部分:
* 回滾段的作用
* 回滾段的類型
* 回滾段的數量、大小及存儲參數
* 回滾段的維護及查詢
* 有關回滾段的常見錯誤及解決方法
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