在Oracle 10.2.0.1.0中,只要設置了undo表空間自動管理,不管有沒開啓自動擴展,不管undo_retention設置爲多少,都會啓用 Automatic Tuning of undoretention的新特性。這個新特性中計算RETENTION算法有問題,會導致unexpired undo數據奇高,並最終導致出現ORA-1628: max # extents 32765reached for rollback segment錯誤。
1 問題現象
Oracle版本:10.2.0.1.0。
浙江現場NQA在執行update/insert操作失敗,ORACLE返回ORA-1628錯誤,並且ORACLE的alert日誌中ORA-1628出現的非常頻繁。錯誤日誌:
Update fail,msg::ORA-01628: max # extents (32765) reached for rollback segment _SYSSMU7$
2 分析
我們在alert日誌中發現了ORA-30036錯誤,出現ORA-30036表示UNDO表空間太小。
ORA-30036的錯誤描述:
Update fail,msg::ORA-30036: unable to extend segment by 8 in UNDOtablespace 'UNDOTBS1'
事務申請undo空間的流程如下:
1) 事務啓動時,ORACLE會爲之分配一個undo segment。ORACLE的設計目標是一個事務對應一個undo segment。ORACLE啓動時,只使得部分undo segments變爲online。隨着事務數量的增多,更多的offline UNDOsegments變爲online狀態。如果所有undo segment已經是online狀態,且undo表空間還有足夠的空間,oracle會創建新undo segment;如果undo表空間的空間已經用盡,幾個事務會合用一個undo segment。
2) 檢查事務對應的undo segment是否有空閒區(extent),如果有直接使用。
3) 如果沒有,使用本segment中處於expired狀態的extent,這些extent存儲了由其它事務提交或回滾留下的過期數據。這個動作稱爲一次擴展(wrap),計入v$rollstat的wraps列。
4) 如果本回滾段中沒有可用的區,從UNDO表空間中請求區。這被稱爲一次Extend,記入v$rollstat的EXTENTS列。
5) UNDO表空間中沒有Free區,從其他回滾段Steal過期的區。Steal的單位一定是以區爲單位。無論Steal是否成功,V$UNDOSTAT的EXPSTEALCNT列都會加1,此列統計的是次數。EXPBLKRELCNT列是成功Steal的塊數。
6) 如果Steal過期區不成功,試圖擴展數據文件。
7) 如果無法擴展文件,在本回滾段中重用未過期的區, V$UNDOSTAT.UNXPBLKREUCNT列增加(增加值是區中塊的個數)
8) 如本回滾段無過期的塊,則Steal其他回滾段中未過期的區。每Steal一次,無論是否成功,都會記入V$UNDOSTAT.UNXPSTEALCNT列。Steal的塊數記入NXPBLKREUCNT列。
9) 如還不行,報ORA-30036錯誤。
於是,創建新的undo 表空間UNDOTBS2,UNDOTBS2包含了4個數據文件,每個文件有31GB。然後切換至新的undo表空間。爲什麼數據文件大小是31G,在block大小爲8K的情況下,數據文件大小限制是32G,有人說數據文件變爲32G後會出現bug,爲了避免這種情況,於是設置數據文件大小爲31G。
【切換undo的表空間的代碼如下】
--查看當前的undo表空間
show parameter undo_tablespace;
--查看所有undo表空間
select * from dba_tablespaces where contents='UNDO';
--查看當前undo表空間對應的文件
select * from dba_data_files wheretablespace_name='UNDOTBS1';
--創建新undo表空間注意修改大小!!!!!!!
create undo tablespace UNDOTBS2datafile
'/opt/oracle/oracle/oradata/orcl/undotbs02_1.dbf' size 31G autoextend off,
'/opt/oracle/oracle/oradata/orcl/undotbs02_2.dbf' size 31G autoextend off,
'/opt/oracle/oracle/oradata/orcl/undotbs02_3.dbf' size 31G autoextend off,
'/opt/oracle/oracle/oradata/orcl/undotbs02_4.dbf' size 31G autoextend off,
extent management local autoallocate retention nogurantee;
--確認是否創建成功
select * from dba_tablespaces where contents='UNDO';
--切換當前表空間
alter system setundo_tablespace=UNDOTBS2 scope=both;
--確認UNDOTBS1上沒有活動的事務,若有,則等待
select usn,xacts,status from v$rollstatwhere xacts != 0;
--刪除舊undo表空間
drop tablespace UNDOTBS1 includingcontents and datafiles cascade constraints;
切換undo表空間4個小時後,出現了一次ORA-01555錯誤,日誌如下:
ORA-01555 caused by SQLstatement below (SQL ID: 0qdadgcckc5xd, Query Duration=1233 sec, SCN:0x0001.c06cbc64):
Fri Dec 28 16:15:51 2012 select tavppfr0_.UMFAILUREREASON as col_0_0_,count(*) as col_1_0_ from tAVP_PFR tavppfr0_ wheretavppfr0_.UMEVENTTIMESTAMP>=1356652518561 group by tavppfr0_.UMFAILUREREASON
Fri Dec 28 16:16:57 2012
ORA-01555是ORACLE中非常常見的錯誤。在undo表空間切換後,舊的undo表空間變爲offline,如果事務訪問舊undo表空間的數據就會得到ORA-01555錯誤。這是因爲offline的undo表空間可能包含了爲達到事務一致性所必需的數據。所以切換表空間的短時間內出現ORA-01555錯誤可以忽略。
悲催的是,切換undo表空間8個小時後出現ORA-01628錯誤。難道是UNDO表空間又滿了? 於是查看處於undo空間分配情況,從表dba_undo_extents可以發現處於active/unexpired/expired狀態的undo數據分別爲81M/35G/1M。
>select tablespace_name,status,SUM (bytes) fromdba_undo_extents GROUP BY tablespace_name,status;
TABLESPACE_NAME STATUS SUM(BYTES)
UNDOTBS2 EXPIRED 1114112
UNDOTBS2 UNEXPIRED 3.5858E+10
UNDOTBS2 ACTIVE 81002496
然後查看了最大和最小的undosegment。最小undosegment只有4個區(extent),只佔用了1M空間,而最大的undo segment中的區(extent)數量已經達到了其最大值32765。
>select SEGMENT_NAME,TABLESPACE_NAME, BYTES/1024/1024, EXTENTS,MAX_EXTENTS from dba_segments whereTABLESPACE_NAME='UNDOTBS2';
SEGMENT_NAME TABLESPACE_NAMEBYTES/1024/1024 EXTENTS MAX_EXTENTS
_SYSSMU84$ UNDOTBS2 24738.6875 32765 32765
_SYSSMU104$ UNDOTBS2 1.1875 4 32765
到這裏,我感到非常困惑。困惑點一,undo表空間是非常富餘的,爲什麼有的undo segment的extents到達了極限32765,而有的undosegment僅僅包含4個extent。困惑點二,UNEXPIRED狀態的undo數據遠遠大於其它狀態的數據量。難道有大事務正在回滾,因爲我測試發現,事務回滾後,其undo 數據會變爲unexpired狀態。通過查詢x$ktuxe視圖,發現回滾事務佔用undo空間僅爲338*block_size=2.5M。注意操作x$ktuxe需要使用sys用戶。
>select sum(ktuxesiz) from x$ktuxe whereKTUXESTA='ACTIVE';
SUM(KTUXESIZ)
-------------
338
可不可以通過修改undosegment的MAXEXTENTS屬性,從而避免這個錯誤。執行後發現UNDO_MANAGEMENT處於AUTO模式下,無法修改器storage參數的。
>alter rollback segment "_SYSSMU84$" storage(MAXEXTENTS 40000);
ORA-02221: invalid MAXEXTENTS storage option value
難道是UNDO_RETENTION調整的非常大導致處於unexpired狀態的數據異常的大?通過查看v$undostat發現最新記錄的TUNED_UNDORETENTION字段僅爲900秒,並且在創建undo表空間指定了retention nogurantee,所以這個原因也排除了。
>slect begin_time,end_time from v$undostat order bybegin_time desc;
至此,一切陷入僵局。只能繼續從業務下手,通過每隔30秒採集數據來找出大事務。腳本見第4小節。一天後發現,最大的事務也只佔用了1G的undo空間。於是開始強烈懷疑是ORACLE自身的bug所至,google一下後發現了ORACLE的10G版本在計算RETENTION算法有bug。於是試着使用其內部參數_undo_autotune關閉其自動調整RETENTION,即執行如下語句。
>alter system set "_undo_autotune" = falsescope=both;
執行上述操作900秒後發現,unexpired狀態從35G下降至169M,且_SYSSMU84$的數量從32765下降到了132。
>select tablespace_name,status,SUM (bytes) fromdba_undo_extents GROUP BY tablespace_name,status;
UNDOTBS2 EXPIRED 232.75
UNDOTBS2 UNEXPIRED 16957.625
UNDOTBS2 ACTIVE 61.9375
>select usn,extents from V$ROLLSTAT;
--只貼出了部分記錄
USN EXTENTS
84 132
至此可以確認,ORACLE的計算retention算法是有bug的。後來諮詢了ORACLE的售前工程師,答覆如下。
Oracle 10gr2的後續版本中添加了撤銷(UNDO)信息最短保留時間段自動調優的特性,不再僅僅依據參數UNDO_RETENTION的設定,(需注意10.2.0.3以下有BUG,10gR2的bug現象爲,只要設置了undo表空間自動管理,不管有沒開自動擴展,不管undo_retention設置爲多少,都會啓用 Automatic Tuning of undo retention的新特性。這個bug在10.2.0.4中已經修復,建議找時間停機打patch)其調優原則如下:
當撤銷表空間(UNDO TABLESPACE)大小固定,Oracle將根據表空間的大小和實際的系統負載動態調整撤銷信息保存時間,該最短保存時間的具體長短基於撤銷表空間大小的一定比例值公式換算後獲得;它總是比設定的UNDO_RETENTION大,當撤銷表空間大量空閒情況下可能遠遠大於UNDO_RETENTION。
當撤銷表空間設定爲自動擴展空間情況下,Oracle將動態調整撤銷信息最短保留時間爲該時段最長查詢時間(MAXQUERYLEN)加上300秒或參數UNDO_RETENTION間的較大者,即MAX((MAXQUERYLEN+300),UNDO_RENTION);同樣的,該最短保存時間可能遠遠大於設定的UNDO_RETENTION。
在自動調整情況下,實際的撤銷信息最短保留時間可以通過查詢V$UNDOSTAT視圖上的TUNED_UNDORETENTION列獲得,在無法就撤銷表空間做相應修改的情況,我們可以通過修改隱式參數” _UNDO_AUTOTUNE”爲FALSE關閉該自動調優特性。以上設定生效後,V$UNDOSTAT視圖上TUNED_UNDORETENTION列不再更新,且撤銷信息最短保留時間固定爲參數UNDO_RETENTION的設定值。該參數可以不用重啓數據庫而動態設置生效。
3 結論
在Oracle 10.2.0.1.0中,只要設置了undo表空間自動管理,不管有沒開自動擴展,不管undo_retention設置爲多少,都會啓用 Automatic Tuning of undoretention的新特性。這個新特性中計算RETENTION算法有問題,會導致unexpired undo數據奇高,並最終導致出現ORA-1628: max # extents 32765reached for rollback segment錯誤。可以通參數_undo_autotune來關閉這個新特性,從而避免問題的產生。