Oracle:UNDO機制
Oracle絕對禁止一個用戶查看另一個用戶未提交的事務數據。
啟動一個DML事務時,已修改數據的象前版本被緩存在database buffer cache,再有一個緩沖副本被寫入一個回退段(undo segment)上。
Undo segment三個重要目的:
1用戶發布rollback命令,則可用來恢復數據原狀態。
2其他用戶訪問DML用戶發布commit前的原數據,則提供一個已修改數據的讀一致性視圖。
3在實例恢復期間,用來rollback一個在實例故障剛發生前進行的未提交的事務。
回滾段由范圍組成,這個范圍由5個或5個以上的Oracle塊組成。
回滾段工作方式:
1在一個回滾段內,以環形方式循環使用范圍,直到段裝滿。由用戶的commit或rollback命令發布后釋放回滾段內的范圍。
2同一個undo segment可以存儲很多個像前版本,同時,一個像前版本只會存儲在一個undo segment而不會因空間等任何問題連接到其他undo segment。
3如果一個undo segment中的一個范圍啟動了一個事務的像前版本,且逐漸增長裝滿了該范圍,此時會環繞到下一個臨近的范圍繼續使用空間,此時如果下一個臨近范圍已經被其他事務占領,則絕對不會跳過該臨近范圍查找其他可用范圍,而是會在這個范圍之間創建一個新的范圍來使用。
4一個undo segment能處理的事務個數,取決于Oracle塊大小。
5每個數據庫都至少有一個回滾段(系統回滾段),一旦創建了其他回滾段,則該回滾段將只用于處理數據目錄讀一致性和事務控制。
6set transaction use rollback segment命令可以申請一個指定的回滾段。
7LOB列不使用undo segment,而使用創建時分配給表的空間來存儲像前版本。
5.5.1 測量UNDO I/O性能
5.5.1.1undo segment頭部的爭用
Oracle 使用undo segment頭部塊中一個事務表來跟蹤使用他的那些事務,其內容通常被緩存在database buffer cache中以便被搜索。OLTP上很有可能會因為訪問這個頭部發生等待。
SQL> select event,total_waits,time_waited,average_wait
from v$system_event where event like '%undo%' and event like '%slot%';
average_wait:平均每毫秒等待的次數,等于0或接近于0最好。
SQL>select class,count from v$waitstat
where class in ('undo header','system undo header');
count:等候訪問undo segment頭部的次數。理想情況下,該值等于0或接近于0最好。
SQL>select n.name,s.usn,
decode(s.waits,0,1,1-(s.waits/s.gets)) "RBS Header Get Ratio"
from v$rollstat s,v$rollname n
where s.usn = n.usn
order by usn;
RBS Header Get Ratio:等于1或接近1最好,至少為95%。
5.5.1.2 undo segment范圍的爭用
SQL>select class,count from v$waitstat
where class ='system undo block' ;
count:系統回滾段的范圍的塊爭用次數
SQL>select w.count,w.count/s.value as wait_ratio from v$waitstat w,v$sysstat s
where w.class = 'undo block'
and s.name = 'consistent gets';
count:非系統回滾段的范圍的塊爭用次數
wait_ratio:回滾等待率,如果超過1%,則需要調整了。
5.5.1.3 undo segment的環繞
SQL> select n.name,s.usn,
decode(s.waits,0,1,1-(s.waits/s.gets)) "RBS Header Get Ratio",s.wraps
from v$rollstat s,v$rollname n
where s.usn = n.usn
order by usn;
s.wraps:該回滾段被環繞到下一個范圍的次數。次數太多表示段范圍可能太小。
5.5.1.4 undo segment的動態范圍分配
事務的像前版本在undo segment中發生環繞,而下一個范圍已經分配給其他事務的像前版本,此時會在他們之間動態創建一個范圍來給該事務的像前版本環繞。應避免以減少I/O。
SQL> select event,total_waits,time_waited,average_wait
from v$system_event where event = 'undo segment extension';
time_waited:表示動態分配的等待次數。若值很高或不斷增長,說明undo segment太少或太小。
SQL>select n.name,s.usn,s.extends,
decode(s.waits,0,1,1-(s.waits/s.gets)) "RBS Header Get Ratio",s.wraps
from v$rollstat s,v$rollname n
where s.usn = n.usn
order by usn;
s.extends:被動態添加的范圍數。若經常發生動態添加,則說明undo segment可能太小。
5.5.2 優化undo segment
優化目標:
1) 用戶不用等待,就始終可找到undo segment來使用。
2) 用戶始終能得到完成事務所需要的讀一致性視圖。
3) 回滾段不會引起過多的I/O。
一般就是:
1) 消除對undo segment header或block的爭用。
2) 盡量最小化undo segment的擴充和環繞。
3) 避免undo segment用盡。
4) 始終擁有為用戶提供一致性視圖的undo segment。
做法:
1) 添加更多的undo segment 。
2) 增大現有undo segment。
3) 明確管理大事務的undo segment。
4) undo segment需求最小化。
5) 使用自動管理功能。
5.5.2.1 增加更多的undo segment
最好把新添加的undo segment放在另外的磁盤的表空間內。對undo要求最多的是delete,其次是update,最后是insert。
Oracle建議,為每4個并發事務創建一個undo segment,最多只能添加20個undo segment.
為了更準確的確定到底需要多大的undo segment,可以跟蹤用戶使用的undo segment大小:
//查詢當前用戶所使用的undo segment大小
SQL>select s.osuser,s.username,t.used_ublk
from v$session s,v$transaction t
where s.taddr = t.addr;
t.used_ublk:單位為Oracle塊,*block size等于該用戶將使用的size。
//查詢某個大事務的具體使用大小。
1) 只保留一個undo segment online,其他的都offline。
Alter rollback segment xxxxx offline;
2) 統計當前所使用了的undo segment大小。
Select n.name,s.sun,s.writes from v$rollname n,v$rollstat s
Where n.usn = s.usn and name != ‘SYSTEM’;
s.writes:有多少字節的數據被寫到該rollback segment上了。
3) 執行遇到回滾問題的大事務。
如:delete from alarminfo;
4) 重新執行“2)”的統計,使用新查詢的s.writes減去(-)“2)”中查詢出來的s.writes值,就是“3)”中事務所要使用的undo segment大小。
如果設置的undo segment大小是按大事務來設置,可能會浪費很多空間,只需要明確管理undo segment就可以了。
典型錯誤:ORA-01555 SNAPSHOT TOO OLD
一個修改事務很長時間未提交,別人查的時候在undo segment中找到了一個像前版本得到一致性讀,別人還在查詢到該修改前,最先修改的人提交了,并且此時undo segment中因為接受了commit而不守護該范圍,此范圍被其他事務寫了。
這時候就會發生這種錯誤,只需要在查一次就可以了。
防范:
1) 表上發生小事務時候,設法避免運行時間很長的查詢。
2) 增加undo segment的大小和數量。
一般設置:initial=512k,next=512k,minextents=20,這樣就會創建一個10M的undo segment.
5.5.2.2 明確管理大事務的undo segment
創建一個很大的undo segment,專用于處理特定的事務。
由于Oracle會自動把任務分配給undo segment,所以一般創建完和使用完后,需要手工把他們offline.
1) 創建大回滾段。
Create private rollback segment rbs_for_large_tran
Storage (initial 10M next 10M) tablespace rbs;
2) 直到在作業開始前,把rbs_for_large_tran 聯機。
Alter rollback segment rbs_for_large_tran online;
或:execute dbms_transaction.use_rollback_segment(‘rbs_for_large_tran’);
3) 啟動作業。
Delete from alarminfo;
4) 一旦"3)"執行完,馬上在另外一個窗口把該rbs_for_large_tran段offline.
Alter rollback segment rbs_for_large_tran offline;
注意:在作業中的任何commit,都將導致rbs_for_large_tran脫機。若脫機了,需重新聯機。
5.5.2.3 undo segment需求最小化
最大限度的減少寫往undo segment的項目數量和大小。
如:
imp的時候使用commit=y.
exp的時候不要使用consistent選項。
Sql*loader時設置適當的commit值。
5.5.2.4 使用自動管理功能
Oracle 9i的新功能,通過配置init.ora,讓Oracle自動來進行管理undo segment(Oracle推薦)。
undo_managementl
=auto //使用undo 自動管理(AUM)
=manual //不使用AUM。
l undo_retention
單位是秒。指定一個像前版本在commit后被保存的時間。(減少ORA-01555錯誤)
l undo_suppress_errors
FALSE,TRUE.指定是否抑制在RBU中可用的命令。
l undo_tablespace
指定用于AUM的表空間名。
(同一時間,只能有一個undo tablespace在線,也必須有一個undo tablespace在線。
如果數據庫未創建而undo_management=auto,則系統自動創建一個SYS_UNDOTBS表空間來使用)
創建的語法:
create undo tablespace undo_tbs
datafile ‘/u01/oradata/prod/undo01.dbf’ size 500M
autoextend on
next 5M maxsize 2000M;
(不能指定初始范圍和下一個范圍大小,因系統要自己指定)
估計undo tablespace大小的公式:
Undo space = (undo_retention * (undo blocks per second * db_block_size)) + db_block_size;
刪除一個大的undo tbs:
8) 創建一個新的undo tbs undo_tbs02.
9) SQL>alter system setundo_tablespace=undo_tbs02;
此時,新的事務會使用undo_tbs02,而以前的事務,依然會繼續使用undo_tbs.
10) 待Undo_tbs上的所有事務commit或rollback,且超過了undo_retention指定的時間后,drop tablespace刪除該undo tbs.
(此時注意,如果drop 了undo_tbs,此時任何發生在undo_tbs的像前讀都要報錯,此時最好發生在alter命令前的所有事物都commit了或rollback了。)
SQL>select u.begin_time,u.end_time,
t.name "undo_tbs_name",
u.undoblks "blocks_used",
u.txncount "transactions",
u.maxquerylen "longest query",
u.expblkreucnt "expired blocks"
from v$undostat u,v$tablespace t
where u.undotsn = t.ts#;
查詢統計時間內,被undo使用的Oracke塊數,發生的事務數,最長的查詢時間,在需要查一致性時有多少塊已經被覆蓋(出現>0的數表示ORA-01555就很可能發生)。