Undo and redo
Oracle最重要的兩部分數據,undo 與redo,redo(重做信息)是oracle在線(或歸檔)重做日志文件中記錄的信息,可以利用redo重放事務信息,undo(撤銷信息)是oracle在undo段中記錄的信息,用於撤銷或回滾事務。
1 redo
重做日志文件redo log,是數據庫的事務日志,oracle維護着2類重做日志,在線重做日志文件和歸檔重做日志文件,歸檔日志文件就是重做日志的副本,系統將日志文件填滿時arch進程會在另一個位置建立一個在線重做日志的副本
每個oracle數據庫至少有2個重做日志組,以便切換日志,每個日志組至少有1個日志組成員,這些在線重做日志文件是以循環寫的方式使用,
2 undo
你對數據庫執行修改時,數據庫會生成undo信息,以便回滾到更改前的狀態,undo用於取消一條語句或一組語句的作用,undo在數據庫內部存放在一組特殊的段中,
為undo段(回滾段 rollback segment),利用undo,數據庫只是邏輯的恢復到原來的樣子,所有修改都邏輯的取消,但是數據結構以及數據塊本身在回滾后可能不大相同,
對於undo生成對於直接路徑操作不適用,直接路徑操作能夠繞過表上的undo生成。
SQL> set autotrace traceonly statistics SQL> select * from t;--not first execute no rows selected Statistics ---------------------------------------------------------- 0 recursive calls 0 db block gets 3 consistent gets 0 physical reads 0 redo size 995 bytes sent via SQL*Net to client 374 bytes received via SQL*Net from client 1 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 0 rows processed SQL> insert into t select * from all_objects; 49789 rows created. SQL> rollback; Rollback complete. SQL> select * from t; no rows selected Statistics ---------------------------------------------------------- 0 recursive calls 0 db block gets 689 consistent gets -----I/O 0 physical reads 0 redo size 995 bytes sent via SQL*Net to client 374 bytes received via SQL*Net from client 1 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 0 rows processed
Insert導致一些塊增加到表的高水位線(HWM),這些塊沒有因為回滾而消失,
select extent_id, bytes, blocks from user_extents
where segment_name = 'X' order by extent_id; 分配給表的存儲空間—這個表沒有使用任何區段
3 undo 跟redo如何協作
盡管undo信息存儲在undo表空間或undo段中,但也會受到redo保護,會把undo信息當成表數據或索引數據一樣,對undo的修改會生成一些redo,將記入重做日志,
將undo數據增加到undo段中,並像其他部分的數據一樣,在緩沖區緩存中得到緩存
Insert-update-delete場景
3.1 insert
Insert語句,都會生成redo跟undo信息,插入發生后,如下圖
緩存了一下已修改的undo塊,索引塊和表數據塊,這些塊得到重做日志緩沖區相應條目的保護
1假象現在系統崩潰,sga全部被清空,但是我們不需要sga的中的任何內容,重啟動時就好像這個事務就沒發生過,沒有將任何修改的塊刷新輸出到磁盤,
也沒有任何redo信息刷新輸出到磁盤,我們不需要這些undo或redo信息來實現實例失敗恢復
2假象:緩沖區緩存已滿
Dbwr進程要把已修改的塊從緩存輸出到磁盤,首先要求lgwr進程將保護這些數據庫的redo條目輸出到磁盤,dbwr在將任何修改的塊輸出到磁盤之前,都必須要求lgwr進程先刷新輸出到redo日志,
3.2 update
Update所帶來的工作與insert大體一樣,不過undo信息量更大,update,要保存系統的前映像,
緩沖區中會有更多的undo塊,為了撤銷update,如果必要,已修改的數據庫表和索引都會存在緩存中,其中重做日志有的已經輸出到磁盤,有的還在redo buffer 中
1 系統崩潰:啟動時,oracle會讀取重做日志,給定系統的當前狀態,利用重做日志文件中對應的插入的redo條目,並利用仍在緩沖區中對應的redo條目,oracle會前滾插入,連接斷開,oracle發現事務從未提交,因此將其回滾,利用undo,
2 應用回滾事務
Oracle發現這個事務的undo信息可能緩存在undo段中,也肯能已經刷新輸出到磁盤,會把und信息應用到緩存中的數據和索引上,不在緩存中,則先要讀入到緩存,恢復其原來的行,並刷新輸出數據文件,
回滾過程不涉及重組日志,只有恢復和歸檔才會讀取重做日志,重做日志是用來寫的,不用於讀,
3 delete
Delete 會生成undo日志,塊將被修改,並把redo日志發送到重做日志緩沖區,與update類似
4 commit
已經修改的塊放在緩沖區緩存中,可能已經輸出到磁盤,重做這個事務所需的全部redo都安全的存放在磁盤上,undo信息會一直存在,除非undo段回繞並重用了這些undo塊,
4 提交和回滾處理
Commit:commit並沒有做太多的工作,
Commit開銷,頻繁提交,會增加與數據庫的往返同學,如果每個記錄都提交,生成的往返通信量會大得多,每次提交時,必須等待redo寫到磁盤,這會導致等待
在commit之前可能:
已經在sga中生成了undo 塊,已經在sga中生成了已修改的數據塊,已經在sga中生成了對應的2想的redo信息,取決於前3項的大小,已經這些花費的時間,前面的數據可能已經輸出到磁盤,已經得到全部鎖需要的鎖
在實際commit時,
1為事務生成一個scn(系統改變號),scn用於保證事務的順序,並支持失敗恢復,scn還用於保證數據庫中的讀一致性和檢查點,每次有人commit,scn都會增加1。
2 Lgwr進程會將重做日志緩沖區中的redo信息全部輸出到磁盤,並把scn記錄到redolog中,這一步已經真正提交,回車v$transaction中刪除。
3 V$lock記錄中我們會話持有的鎖全部釋放。4 如果事務修改的某些塊還在緩沖區存在,則已一種快速的模式訪問並清理,塊清除。
Rollback 。撤銷所有的修改,從undo中讀取數據,逆向執行前面所做的事情,釋放所持有的鎖,
5 分析redo
Redo管理是數據庫的一個串行點,任何oracle實例只有一個lgwr,
5.1 測量redo
SQL> set autotrace traceonly statistics; SQL> truncate table t; Table truncated. SQL> insert into t select * from all_objects; 49788 rows created. Statistics ---------------------------------------------------------- 6702 recursive calls 6822 db block gets 83402 consistent gets 2 physical reads 5625720 redo size 672 bytes sent via SQL*Net to client 575 bytes received via SQL*Net from client 4 SQL*Net roundtrips to/from client 14 sorts (memory) 0 sorts (disk) 49788 rows processed SQL> truncate table t; Table truncated.
對於noarchivelog模式的數據庫進行直接路徑加載,如果是archivelog模式,必須把表設置為nologging模式
SQL> insert /*+ APPEND */ into t select * from all_objects; 49788 rows created. Statistics ---------------------------------------------------------- 6244 recursive calls 1254 db block gets 82000 consistent gets 2 physical reads 57016 redo size 659 bytes sent via SQL*Net to client 589 bytes received via SQL*Net from client 4 SQL*Net roundtrips to/from client 1 sorts (memory) 0 sorts (disk) 49788 rows processed
5.2 能關掉重做日志程序嗎
答案是不能的,重做日志是必不可少的,dba如果把db設置為force logging模式,在這種情況下,任何操作都會記錄日志,
select force_logging from v$database
5.2.1 在sql中設置nologging
有些sql語句和操作支持使用nologging,有些特定的操作會比不使用nologging生成的redo要少的多,
在歸檔模式下,創建表使用nologging,看與不適用nologging的redo的比較
查看歸檔模式: cmd》archive log list
select log_mode from v$database
修改到歸檔模式:1 alter system set log_archive_dest_1=’location=f:\
Mydb\acrhive’;將歸檔日志放到指定的目錄,默認在安裝rdbms目錄下
關閉 shutdown immediate
startup mount 模式
alter database archivelog
alter database open
歸檔未歸檔的文件 alter system archive log all
SQL> alter system set log_archive_dest_1='location=f:\mydb\archivelog';
System altered.
創建一個函數,返回redo的大小
create or replace function get_stat_val( p_name in varchar2 ) return number as l_val number; begin select b.value into l_val from v$statname a, v$mystat b where a.statistic# = b.statistic# and a.name = p_name; return l_val; end;
create table t
NOLOGGING as select * from all_objects
Nologging注意:
1 還是會生成一定的redo,該redo主要是保護數據字典
2 此時nologging在建表時避免生成redo,但是在后續操作中insert update,delete都會生成redo
3 在archivelog模式的數據庫中,使用了nologging之后,要趕快備份操作所創建的數據
5.2.2 在索引上使用nologging
在noarchivelog模式下,
create index test_y on test2(y) select get_stat_val('redo size') from dual
alter index test_y rebuild
select get_stat_val('redo size') from dual 與 alter index test_y nologging 之后,redo相差不多
在noarchivelog模式下,索引create個rebuild不會記錄到redo中
在archivelog下才有區別
Nologging小結:采用nologging模式執行以下操作
1 索引的創建和alter(重建)
2 表的批量insert(用過append直接路徑加載或sqlldr直接路徑加載),表數據不生成redo,但是索引的所有修改會生成redo
3 lob操作(對大對象的操作不需要生成redo)
4 用過create as select 創建table
5 各種alter table 操作
6 臨時表和redo/undo
臨時表不會為他們的塊生成redo信息,對臨時表的操作是不可恢復的,修改臨時表的一個塊時不會將修改記錄到重做日志中,不過臨時表會生成undo,
臨時表可以有約束,正常表有的臨時表都可以有,
關於臨時表的dml操作
Insert 不會生成undo
Update生成的undo比正常表少一半
Delete會生成一樣的undo
另外考慮臨時表上的索引,索引的修改也會生成undo
7 分析undo
7.1 什么操作會生成最多最少的undo
存在索引,對表的操作會生成更多的undo,insert最少,delete最多的undo,
大量更新前,索引刪除后在重建
7.2 ora-1555 snapshot too old 快照太舊
1555錯誤與數據破壞跟數據丟失沒有關系,唯一的影響是不能進行查詢處理
導致1555的3個原因
1 undo段太小,不足以在系統下執行工作
2程序跨commit獲取
3 塊清除
由於數據庫的讀一致性,導致會從undo中讀取信息,undo太小,被回繞利用,查詢就不能繼續,
解決辦法:1 適當的設置undo_retention(要大於運行最長事務的所需的時間),可以用v$undostat undostat來確定長時間運行的查詢的持續時間,另外保證磁盤上預留了足夠的空間,使undo段能根據所需要的undo_retention字段增加
2使用手動管理undo段增加其大小,建議自動管理undo段
3減少查詢的時間(調優),最好的辦法
4 收集相關的統計信息,大批量的update跟insert會導致塊清除,在大量更新后,收集表的統計信息
select * from v$undostat
1 undo段確實太小
create undo tablespace undo_small
datafile '/tmp/undo.dbf' size 2m autoextend off 創建一個小的undo空間
alter system set undo_tablespace = undo_small;
create table t as select * from all_objects order by dbms_random.random;
exec dbms_stats.gather_table_stats( user, 'T', cascade=> true );收集統計信息
進行一個大量的更新,同時在另一個會話查詢,
把 回滾表空間設置為自動增長(相應的數據文件)
alter database datafile '/tmp/undo.dbf' autoextend on next 1m maxsize 2048m;
查看大小 select bytes/1024/1024 from dba_data_files where tablespace_name = 'UNDO_SMALL'
解決辦法:1 保證事務大小適當,不要過於頻繁的提交
2 使用dbms_stats掃描相關的對象
3 允許undo表空間自動擴大,
4 減少查詢的時間(先嘗試做)
Undo信息有專門的undo表空間(對於的數據文件),使用undo信息前必須在緩沖區中sga中,可能在磁盤的undo信息要讀到緩沖區中使用,undo是可繞回使用,可自動擴展