1、什么是undo
當我們對數據執行修改操作時,數據庫會生成undo 信息,這樣一旦執行的事務或語句由於某種原因失敗,或者發出一條ROLLBACK 語句請求回滾,就可以利用這些undo信息將數據放回到修改前的樣子。undo 信息存儲在一組特殊的段中,這稱為undo 段(undo segment)。
select segment_name,tablespace_name,status,block_id,file_id from dba_rollback_segs;
2、undo有什么作用
在Oracle數據庫中,undo主要有三大作用:提供一致性讀(Consistent Read)、回滾事務(Rollback Transaction)以及實例恢復(Instance Recovery)。
2.1、一致性讀(Consistent Read)
一致性讀是相對於臟讀(Dirty Read)而言的。假設某個表T中有10000條記錄,獲取所有記錄需要15分鍾時間。當前時間為9點整,某用戶A發出一條查詢語句:select * from T,該語句在9點15分時執行完畢。當用戶A執行該SQL語句到9點10分的時候,另外一個用戶B發出了一條delete命令,將T表中的最后一條記錄刪除並提交了。那么到9點15分時,A用戶將返回多少條記錄?正確的應該是10000條記錄。很明顯,在 9點鍾那個時間點發出查詢語句時,表T中確實有10000條記錄,只不過由於I/O的相對較慢,所以才會花15分鍾來完成所有記錄的檢索。
這是因為ORACLE通過構造CR(consistent read)塊提供一致讀的方式,保證了數據的完整性。具體的實現方式如下:
用戶在進行查詢時oracle會先記錄當時的SCN號,當查詢讀到被B用戶更新刪除記錄后的塊時,發現這個塊頭部ITL槽中的SCN號比查詢開始時的SCN號大,這樣就會在回滾段中尋找這個塊的前映像數據,找到這個數據的前映像后,把前映像和CURRENT的數據塊進行合並,就形成了一個CR BLOCK,通過查詢CR BLOCK就可以得到一致性的數據。
2.2、回滾事務(Rollback Transaction)
當用戶發出一個update 操作后,會在回滾段中記錄更新記錄的前映像數據,如果用戶再發出rollback命令撤銷數據變化時,Oracle利用記錄在ITL槽里記錄的undo 塊的地址找到該undo塊,然后從中取出變化前的值,並放入數據塊中,從而對事務所作的變化進行回滾。
2.3、實例恢復(Instance Recovery)
實例恢復則是在SMON進程完成前滾並打開數據庫以后發生。SMON進程會去查看undo segment頭部(所謂頭部就是undo segment里的第一個數據塊)記錄的事務表(每個事務在使用undo塊時,首先要在該undo塊所在的undo segment的頭部記錄一個條目,該條目里記錄了該事務相關的信息,其中包括是否提交等),將其中既沒有提交也沒有回滾,而是在實例崩潰時被異常終止的事務全部回滾。
3、回滾段管理方式
oracle的undo管理有兩種方式,自動管理和手動管理,可以使用 undo_management 參數來進行控制。當設置為auto時使用undo表空間的方式來自動管理undo段,如果設置為manual則使用rollback segment方式存儲undo信息。
oracle9i 以后開始使用undo自動管理,手動管理方式只在特殊的場合下才會偶爾使用到。后面在介紹undo恢復時再做相應介紹。
說到自動管理,首先要介紹UNDO_RETENTION參數,這個參數用來指定undo 記錄保存的最長時間,默認為900秒,可以動態的進行修改。這個參數不是指undo中的數據在undo表空間中一定要保存900秒,例如當一個新事務開始的時候,如果發現undo表空間已經被寫滿,則新事務的數據會自動覆蓋已提交事務的數據,而不管這些數據是否已存放夠900秒。同時也不是指undo_retention 中指定的時間一過,已經提交事務中的數據就立刻無法訪問,通常它只是失效,只要沒有被別的事務覆蓋,它就會仍然存在,這時我們可以使用flashback 特性查看表在更新前不同時刻的數據。
因此UNDO表空間中的數據在不同時段就會有不同的狀態顯示,在dba_undo_extents 數據字典里記錄了UNDO中每個區段的狀態.
SQL> SELECT tablespace_name, status, SUM (bytes) / 1024 / 1024 "Bytes(M)"
FROM dba_undo_extents
GROUP BY tablespace_name, status;
TABLESPACE_NAME STATUS Bytes(M)
---------------- --------- ----------
UNDOTBS2 EXPIRED 1.4375
UNDOTBS2 UNEXPIRED .375
UNDOTBS2 ACTIVE .125
UNDOTBS1 EXPIRED 22
其中:
ACTIVE :未提交的回滾數據,該數據所關聯的事務並未提交,用於實現讀一致性,所以該數據不能被其它事務的數據所覆蓋 。
UNEXPIRED:已經提交但未過期的回滾數據,該數據關聯的事務已經提交,但是仍受到undo retention參數保持時間的影響,當undo表空間中沒有可用的數據塊時,這些數據塊會直接被覆蓋而進行重用。
EXPIRED: 事務已經提交,而且數據保存時間已經超過undo retention參數指定的時間,屬於已經過期的數據.可以被隨時重用。
需要說明的是,當設置表空間啟動Guarantee特性時,UNEXPIRED類型的塊就必須要等到undo_retention 指定時間過期后才能被重用。
4、undo段的常見問題及維護操作
4.1、Oracle ORA-01555 快照太舊
ORA-01555是oracle中常見的一個錯誤,例如當我們進行的事務需要使用undo數據來構造CR塊的時候,如果對應的undo信息已經被覆蓋時,就會報出ora-01555錯誤。
對於這種情況,通常有以下幾種解決辦法。
1、優化出錯的SQL,減少語句的查詢時間。
2、增加UNDO表空間大小
3、增加undo_retention 時間,默認只有15分鍾
4、避免頻繁的提交
4.2、Oracle ORA-1628 回滾段達到32765最大值
undo表空間自動管理方式下,單個回滾段的最大區數為32765,我們知道一個事務只能使用一個回滾段,當一個大事務在進行大量數據更新時,所使用的回滾段不能完全存放這個事務的前映像數據時, 就可能會出現這種情況,這種情況下只能通過切換undo表空間來排除故障。避免這種故障的根本原因還是要優化相應的SQL語句,減少UNDO塊的產生。
出現ORA-1628故障時至少會有一個回滾段的狀態會顯示為FULL,如下表信息:
SQL> select segment_name,max_extents,status from dba_rollback_segs;
SEGMENT_NAME MAX_EXTENTS STATUS
----------------------- ----------- --------
SYSTEM 32765 ONLINE
_SYSSMU1$ 32765 OFFLINE
_SYSSMU2$ 32765 OFFLINE
_SYSSMU3$ 32765 FULL
_SYSSMU4$ 32765 OFFLINE
_SYSSMU5$ 32765 OFFLINE
_SYSSMU6$ 32765 OFFLINE
4.3、UNDO表空間損壞后的數據恢復
如果數據庫在運行過程中出現undo表空間故障時,通常可以通過在線新建undo表空間,並且切換為默認表空間后就能快速解決故障。假如undo損壞而且數據庫在關閉狀態,如果沒有任何有效的備份時,可以通過修改undo手動管理的方式來啟動數據庫。
這里用一個案例來說明恢復的過程。
系統環境: oracle 10.2.0.4 非歸檔方式,無有效備份
故障現象:
數據庫無法啟動,alert日志中有大量的ora-600 錯誤
ORA-00600: internal error code, arguments: [2662], [1], [3008810928], [1], [3008849117], [8388617], [], []
ORA-00600: internal error code, arguments: [4194], [15], [6], [], [], [], [], []
Mon May 23 18:36:46 2011
Doing block recovery for file 2 block 336654
Block recovery from logseq 2200, block 63 to scn 7317545555
故障分析:
根據提示信息,可以查到是undo表空間的數據文件出現壞塊造成數據庫無法啟動,因數據庫為非歸檔方式,沒有有效的undo備份文件,這種情況下就可以使用undo手動管理的方式啟動數據庫,詳細可以參考Metalink文章[ID 281429.1] 。
下面是大概的步驟介紹。
1、啟動到nomount 狀態,備份參數文件
2、修改參數文件,undo_management=manual
3、使用新的參數文件啟動數據庫
4、創建新的回滾段
SQL> create rollback segment r01;
5、設置回滾段為online
SQL> alter rollback segment r01 online;
6、創建表空間undotbs2
7、停庫,修改參數為表空間自動管理,
8、重啟啟動數據庫。