第六章重做(Redo)
Redo的內(nèi)容 Oracle通過Redo來實現(xiàn)快速提交,一方面是因為Redo Log File可以連續(xù)、順序地快速寫出,另一個方面也和Redo記錄的精簡內(nèi)容有關(guān)。 兩個概念: 改變向量(Change Vector) 改變向量表示對數(shù)據(jù)庫內(nèi)某一個數(shù)據(jù)塊所做的一次變更。改變向量中包含了變更的數(shù)據(jù)塊的版本號、事務(wù)操作代碼、變更從屬數(shù)據(jù)塊的地址(DBA)以及更新后的數(shù)據(jù)。例如:一個update事務(wù)包含一系列的改變向量,對于數(shù)據(jù)塊的修改是一個向量,對于回滾段的修改又是一個向量。 重做記錄(Redo Record) 重做記錄通常由一組改變向量組成,是一個改變向量的集合,代表一個數(shù)據(jù)庫的變更(INSERT、UPDATE、DELETE等操作),構(gòu)成數(shù)據(jù)庫變更的最小恢復(fù)單位。例如:一個Update的重做記錄包括相應(yīng)的回滾段的改變向量和相應(yīng)的數(shù)據(jù)塊的改變向量等。 假定發(fā)出一個更新語句: Update emp set sal=4000 where empno=7788; 這個語句的執(zhí)行如下所示: 檢查empno=7788記錄在Buffer Cache中是否存在,如果不存在則讀取到Buffer Cache中; 在回滾段表空間的相應(yīng)回滾段事務(wù)表上分配事務(wù)槽,這個操作需要記錄Redo信息; 從回滾段讀入或者在Buffer Cache中創(chuàng)建sal=3000的前鏡像,這需要產(chǎn)生Redo信息并記入Redo Log Buffer; 修改sal=4000,這是update的數(shù)據(jù)變更,需要記入Redo Log Buffer; 當用戶提交時,會在Redo Log Buffer記錄提交信息,并在回滾段標記該事務(wù)為非激活。 對于數(shù)據(jù)塊的修改,如果執(zhí)行寫出,那么通常需要寫出8KB的Block,而對于Redo日志來說,重做信息卻相當精簡,Oracle只需要記錄那些重構(gòu)事務(wù)必須的信息(如事務(wù)號、文件號、塊號、行號、字段等)即可,這個數(shù)據(jù)量大大減少。 產(chǎn)生多少Redo 在SQL*Plus中使用autotrace功能 當在SQL*Plus中啟用autotrace跟蹤后,在執(zhí)行了特定的DML語句時,Oracle會顯示該語句的統(tǒng)計信息,其中,Redo size一欄表示的就是該操作產(chǎn)生的Redo的數(shù)量: SQL> set autotrace trace stat SQL> insert into test 2 select empno,ename from scott.emp; 已創(chuàng)建12行。 Statistics ---------------------------------------------------------- 189 recursive calls 2 db block gets 37 consistent gets 4 physical reads 564 redo size 778 bytes sent via SQL*Net to client 823 bytes received via SQL*Net from client 4 SQL*Net roundtrips to/from client 1 sorts (memory) 0 sorts (disk) 12 rows processed 通過v$mystat查詢: Oracle通過v$mystat視圖記錄當前session的統(tǒng)計信息,也可以從該視圖中查詢得到session的Redo生成情況: SQL> col name for a30 SQL> select a.name,b.value 2 from v$statname a,v$mystat b 3 where a.statistic#=b.statistic# and a.name='redo size'; NAME VALUE ------------------------------ ---------- redo size 5000 SQL> insert into test 2 select empno,ename from scott.emp; 已創(chuàng)建12行。 SQL> select a.name,b.value 2 from v$statname a,v$mystat b 3 where a.statistic#=b.statistic# and a.name='redo size'; NAME VALUE ------------------------------ ---------- redo size 5564 SQL> select 5564-5000 from dual; 5564-5000 ---------- 564 通過v$sysstat查詢: 對于數(shù)據(jù)庫全局redo的生成量,可以通過v$sysstat視圖來查詢得到: SQL> col value for 999999999999 SQL> select name,value from v$sysstat 2 where name='redo size'; NAME VALUE ------------------------------ ------------- redo size 11471552 從v$sysstat視圖中得到的是自數(shù)據(jù)庫實例啟動以來累計日志生成量,可以根據(jù)實例啟動時間來大致估算每天數(shù)據(jù)庫的日志生成量: SQL> select 2 (select value/1024/1024/1024 from v$sysstat where name='redo size')/ 3 (select sysdate-(select startup_time from v$instance) from dual) 4 redo_gb_per_day from dual; REDO_GB_PER_DAY --------------- .073238253 Redo寫的觸發(fā)條件: 每3秒鐘超時(Timeout) 當LGWR處于空閑狀態(tài)時,它依賴于rdbms ipc message等待,處于休眠狀態(tài),直到3秒超時時間到,如果LGWR發(fā)現(xiàn)有Redo需要寫出,那么LGWR將執(zhí)行寫出操作,log file parallel write等待時間將會出現(xiàn)。 啟用10046事件,從LGWR跟蹤文件中可以觀察到這些事件。 域值達到: 兩個觸發(fā)日志寫的條件: Redo Log Buffer 1/3滿; Redo Log Buffer具有1MB臟數(shù)據(jù)。 這兩者都是限制條件,在觸發(fā)時是協(xié)同生效的。 只要有進程在Log Buffer中分配和使用空間,已經(jīng)使用的Log Buffer的數(shù)量將被計算。如果使用的塊的數(shù)量大于或等于一個隱含參數(shù)_log_io_size的設(shè)置,那么將會觸發(fā)LGWR寫操作。如果此時LGWR未處于活動狀態(tài),那么LGWR將被通知去執(zhí)行后臺寫操作。 缺省的_log_io_size等于1/3log buffer大小,上限值為1MB,此參數(shù)在X$KSPPSV中顯示的0值意為缺省值。 也就是LGWR將在Min( 經(jīng)常有人推薦Log Buffer設(shè)置為3MB大小,就是因為當Redo Log Buffer為3MB時,以上兩個條件可能同時達到。 用戶提交 當一個事務(wù)提交時,在Redo Stream中將記錄一個提交標志。在這些Redo被寫到磁盤上之前,這個事務(wù)是不可恢復(fù)的。所以在事務(wù)返回成功標志給用戶前,必須等待LGWR寫完成。進程通知LGWR寫,并且以Log File Sync事件開始休眠,超時時間為1秒。 Oracle的隱含參數(shù)_wait_for_sync參數(shù)可以設(shè)置為false來避免Redo File Sync的等待,但是將無法保證事務(wù)的恢復(fù)性。 存在一個SGA變量用以記錄Redo線程序要同步的Log Block Number。如果多個提交在喚醒LGWR之前發(fā)生,此變量記錄最高的Log Block Number,在此之前的所有Redo都將被寫入磁盤,這有時被稱為組提交。 在DBWn寫之前 Redo Log Buffer的大小設(shè)置 Redo Log Buffer的大小由初始化參數(shù)LOG_BUFFER定義,該參數(shù)的缺省值為: MAX(512KB,128KB*CPU_COUNT) 通常這個缺省值是足夠的,由于Redo Log Buffer的寫出操作相當頻繁,所以過大的Log Buffer設(shè)置通常是沒有必要的。如果缺省值不能滿足要求,一般來說3MB是一個較合理的調(diào)整開端。 log_buffer參數(shù)的設(shè)置是否需要調(diào)整,可以從數(shù)據(jù)庫的等待事件來判斷: SQL> select event#,name from v$event_name where name='log buffer space'; EVENT# NAME ---------- ---------------------------------------------------------------- 178 log buffer space 當Log Buffer Space等待事件出現(xiàn)并且較為顯著時,可以考慮增大Log Buffer以縮減競爭。 Commit做了什么 當完成事務(wù)操作,發(fā)出Commit命令之后,隨后會收到一個反饋“Commit complete”。 提交完成,意味著Oracle已經(jīng)將此時間點之前的Redo寫入重做日志文件中,這個日志寫完成之后,Oracle可以釋放用戶去執(zhí)行其他任務(wù)。如果此后發(fā)生數(shù)據(jù)庫崩潰,那么Oracle可以從重做日志文件中恢復(fù)這些提交過的數(shù)據(jù),從而保證提交之后的數(shù)據(jù)不會丟失。 日志的狀態(tài) CURRENT:指的是當前的日志文件,該日志文件是活動的,當前正在被使用的,在進行崩潰恢復(fù)時,Current的日志文件時必須的。 ACTIVE:活動的非當前日志,該日志可能已經(jīng)完成歸檔也可能沒有歸檔,活動的日志文件在Crash恢復(fù)時會被用到。 ACITVE狀態(tài)意味著檢查點尚未完成,如果日志文件循環(huán)使用再次到達該文件,數(shù)據(jù)庫將處于等待的停頓狀態(tài),此時在alert文件中,可以看到類似如下記錄:Checkpoint not complete 當這種問題出現(xiàn)時,可以從數(shù)據(jù)庫內(nèi)部通過v$session_wait來觀察,該視圖會顯示數(shù)據(jù)庫當前哪些session正處于這種等待。 Checkpoint not complete在數(shù)據(jù)庫中體現(xiàn)為等待事件log file switch(checkpoint incomplete): SQL> select sid,event,state from v$session_wait; 在此同時,可能DBWR進程正在進行db file parallel write,日志文件必須等待DBWR完成檢查點觸發(fā)的寫操作之后才能被覆蓋。如果設(shè)置了參數(shù)log_checkpoints_to_alert為TRUE的話,還可以在alert文件中清晰地看到檢查點的增進和完成情況。 引起Checkpoint Incomplete可能有以下多種原因: 日志文件過小,切換過于頻繁; 日志組太少,不能滿足正常業(yè)務(wù)量的需要; 日志文件所在磁盤I/O存在瓶頸,導(dǎo)致寫出緩慢,阻塞數(shù)據(jù)庫正常運行; 由于數(shù)據(jù)文件磁盤I/O瓶頸,DBWR寫出過于緩慢; 由于事務(wù)量巨大,DBWR負載過高,不堪重負。 解決方法: 適當增加日志文件大??; 適當增加日志組數(shù); 使用更快的磁盤存儲日志文件(如采用更高轉(zhuǎn)速磁盤;使用RAID10而不是RAID5等方式); 改善磁盤I/O的性能; 使用多個DBWR進程或使用異步I/O等。 注意:Checkpoint Incomplete是一類嚴重的等待,它意味著數(shù)據(jù)庫不能再產(chǎn)生日志,所有數(shù)據(jù)庫修改操作將全部掛起。 INACTIVE:非活動日志,該日志在實例恢復(fù)時不再需要,但是在介質(zhì)恢復(fù)時可能會用到。INACTIVE狀態(tài)的日志也可能沒有被歸檔。如果數(shù)據(jù)庫啟動在歸檔模式,在未完成歸檔之前,日志文件也不允許被覆蓋,這時候活動進程會處于log file switch(archiving needed)等待之中。 日志是否完成歸檔,可以根據(jù)v$log視圖的archived字段進行判斷。 UNUSED:是指該日志從未被寫入,這類日志可能是剛被添加到數(shù)據(jù)庫或者在RESETLOGS之后被重置。被使用之后,該狀態(tài)會被改變。 日志塊的大?。?/SPAN> 初始化參數(shù)LOG_BUFFER決定了Redo Log Buffer的大小,這個參數(shù)的缺省值為MAX(512KB,128KB*CPU_COUNT)。 雖然LOG_BUFFER中的Redo Entries的大小是以bytes為單位,但是LGWR仍然以block為單位把redo寫入磁盤,Redo Block Size是Oracle源代碼中固定的,通常的操作系統(tǒng)都是以512bytes為單位。 可以從v$sysstat中的統(tǒng)計信息中通過計算粗略得到,主要有以下幾個統(tǒng)計信息: Redo Size:Redo信息的大小; Redo Wastage:浪費的Redo的大小; Redo Block Written:LGWR寫出的Redo Block的數(shù)量; 額外的信息:每個Redo Block Header需要占用16bytes。 SQL> select name,value from v$sysstat 2 where name in('redo size','redo wastage','redo blocks written'); NAME VALUE ---------------------------------------------------------------- ---------- redo size 6298004 redo wastage 1458232 redo blocks written 15641 SQL> select ceil(16+(6298004+1458232)/15641) rbsize from dual; RBSIZE ---------- 512 在Linux/UNIX下,Oracle還提供另外一個命令行工具可以用于檢查文件的Block Size大?。?/SPAN>dbfsize 也可以通過轉(zhuǎn)儲日志文件的方式來獲取日志文件塊大小: SQL> alter session set events 'immediate trace name redohdr level 10'; LOG FILE #1: (name #3) D:\ORACLE\ORADATA\GVORA\REDO01.LOG Thread 1 redo log links: forward: 2 backward: 0 siz: 0x32000 seq: 0x Archive links: fwrd: 0 back: 0 Prev scn: 0x0000.0060b530 Low scn: 0x0000.00620001 05/03/2009 18:41:45 Next scn: 0x0000.00635173 05/04/2009 08:57:50 日志文件的大?。?/SPAN> 當日志文件發(fā)生切換時,會觸發(fā)一個檢查點,那么日志文件的大小就和檢查點的觸發(fā)頻率相關(guān)。頻繁的檢查點操作可以縮短數(shù)據(jù)庫的恢復(fù)時間,但是過于頻繁的檢查點卻會帶來性能負擔。所以如何合理的設(shè)置日志文件的大小也是數(shù)據(jù)庫優(yōu)化的一個重要內(nèi)容。 一般來說,在實際生產(chǎn)環(huán)境中,把log switch的時間控制在半小時左右即可。 為什么熱備份期間產(chǎn)生的Redo要比正常的多: 在數(shù)據(jù)庫處于熱備份狀態(tài)時,會產(chǎn)生比平常更多的日志。這是因為在熱備份期間,Oracle為了解決SPLIT Block的問題,需要在日志文件中記錄修改的行所在的數(shù)據(jù)塊前鏡像,而不僅僅是修改信息。 簡單了解一下SPLIT Block的概念: Oracle的數(shù)據(jù)塊是由多個操作系統(tǒng)塊組成。通常UNIX文件系統(tǒng)使用512bytes的數(shù)據(jù)塊,而Oracle使用8KB的db_block_size。當熱備份數(shù)據(jù)文件時,要使用文件系統(tǒng)的命令工具(cp)拷貝文件,并且使用文件系統(tǒng)的blocksize讀取數(shù)據(jù)文件。 這種情況下,可能出現(xiàn)如下狀況:當拷貝數(shù)據(jù)文件的同時,數(shù)據(jù)庫正好向數(shù)據(jù)文件寫數(shù)據(jù)。這就使得拷貝的文件中包含這樣的database block,它的一部分OS Block來自于數(shù)據(jù)庫向數(shù)據(jù)文件(這個DB Block)寫操作之前,另一部分來自于寫操作之后。對于數(shù)據(jù)庫來說,這樣的Block本身并不一致,而是一個分裂塊(SPLIT Block)。這樣的分裂塊在恢復(fù)時并不可用(會提示Corrupted Block)。 所以在熱備份狀態(tài)下,對于變更的數(shù)據(jù),Oracle需要在日志中記錄整個變化的數(shù)據(jù)塊的前鏡像。這樣如果在恢復(fù)的過程中,數(shù)據(jù)文件中出現(xiàn)分裂塊,Oracle就可以通過日志文件中的數(shù)據(jù)塊的前鏡像覆蓋備份,以完成恢復(fù)。 分裂塊產(chǎn)生的根本原因在于備份過程中引入了操作系統(tǒng)工具(如cp工具等),操作系統(tǒng)工具無法保證Oracle數(shù)據(jù)庫的一致性。如果使用RMAN備份,由于RMAN可以通過反復(fù)對區(qū)獲得一致的Block,從而可以避免Split Block的生成,所以不會生成額外的Redo。因此建議在備份時(特別是繁忙的數(shù)據(jù)庫),應(yīng)該盡量采用RMAN備份。 能否不生成Redo NOLOGGING對于數(shù)據(jù)庫的影響 正常的數(shù)據(jù)庫必須生成Redo,這是數(shù)據(jù)庫的機制,否則數(shù)據(jù)庫在遇到故障或Crash時則無法恢復(fù)。但是Oracle為了增強某些特殊操作的性能,對于一些SQL語句,Oracle允許使用NOLOGGING子句,NOLOGGING可以使得日志生成大幅降低,但是必要日志(比如對于字典表的修改)仍然會被記錄。 可以使用NOLOGGING的環(huán)境非常有限,在以下操作中,可以增加NOLOGGING子句: 創(chuàng)建索引或重建索引時; 通過/*+append */提示,使用直接路徑批量INSERT操作或SQL*Loader直接路徑加載數(shù)據(jù); CTAS方式創(chuàng)建數(shù)據(jù)表時; 大對象(LOB)的操作; 一些Alter table操作,如move、split等。 需要注意的是,由于NOLOGGING操作會導(dǎo)致對于數(shù)據(jù)的操作不記錄日志,如果數(shù)據(jù)庫崩潰,這部分數(shù)據(jù)是無法恢復(fù)的,所以通常的建議是,在進行了NOLOGGING操作之后,需要對數(shù)據(jù)庫進行備份,以避免數(shù)據(jù)因數(shù)據(jù)庫實效而丟失。 disable_logging對于數(shù)據(jù)庫的影響 Oracle存在一個內(nèi)部參數(shù),可以使數(shù)據(jù)庫關(guān)閉日志記錄,從而實現(xiàn)某些特殊需要或測試目的,這個參數(shù)是_disable_logging,可以動態(tài)設(shè)置這個參數(shù)。 由于不記錄日志,在進行數(shù)據(jù)庫恢復(fù)時,這些數(shù)據(jù)是無法恢復(fù)的。 如果數(shù)據(jù)庫運行在歸檔模式下,設(shè)置該參數(shù)會導(dǎo)致日志文件損壞。因為在設(shè)置該參數(shù)之后,歸檔進程無法識別該日志文件格式,會將該日志文件標記為損壞。 設(shè)置了_disable_logging參數(shù),可以禁用日志的生成,從而提高某些測試的性能。 Force logging(強制日志)模式 當使用Dataguard作為數(shù)據(jù)庫的備份或容災(zāi)高可用性手段時,通常日志就變得不可缺少。在Oracle 9iR2中,可以將數(shù)據(jù)庫置于強制日志模式(Force Logging Mode)。在強制日志模式下,所有操作都將記錄日志。 Redo故障的恢復(fù) 丟失非活動日志組的故障恢復(fù): 如果數(shù)據(jù)庫丟失的是非活動(INACTIVE)日志組,由于非活動日志組已經(jīng)完成檢查點,數(shù)據(jù)庫不會發(fā)生數(shù)據(jù)損失,此時只需要通過clear重建該日志組即可。 清除日志組的命令:alter database clear logfile group 2; 如果數(shù)據(jù)庫處于歸檔模式下,并且該日志組未完成歸檔則需要使用如下命令強制清除: Alter database clear unarchived logfile group 2; 丟失活動或當前日志文件的恢復(fù): 在損失當前日志時,數(shù)據(jù)庫是正常關(guān)閉的: 由于關(guān)閉數(shù)據(jù)庫前,Oracle會執(zhí)行全面檢查點,當前日志在實例恢復(fù)中可以不再需要。直接clear即可。 在Oracle 9i中,可能無法對當前日志進行clear,需要通過until cancel恢復(fù)后,resetlogs打開。 在損失當前日志時,數(shù)據(jù)庫是異常關(guān)閉的: 如果在損失當前日志時,數(shù)據(jù)庫是異常關(guān)閉的,那么Oracle在進行實例恢復(fù)時必須要求當前日志,否則Oracle將無法保證提交成功的數(shù)據(jù)不丟失(也就意味著Oracle會丟失數(shù)據(jù)),在這種情況下,Oracle數(shù)據(jù)庫將無法啟動。 對于這種情況,通常需要從備份中恢復(fù)數(shù)據(jù)文件,通過應(yīng)用歸檔日志文件向前推演,直到最后一個完好的日志文件,然后通過resetlogs啟動數(shù)據(jù)庫完成恢復(fù)。丟失的數(shù)據(jù)就是損壞的日志文件中的數(shù)據(jù)。 可是不幸的是,很多數(shù)據(jù)庫是從來不備份的,那么在面對這種情況時,Oracle提供了一種內(nèi)部手段可以用來強制性數(shù)據(jù)庫打開、忽略一致性等問題。在打開數(shù)據(jù)庫之后,Oracle建議導(dǎo)出數(shù)據(jù),然后重建數(shù)據(jù)庫,再導(dǎo)入數(shù)據(jù),完成災(zāi)難恢復(fù)。 SQL> select ksppinm,ksppdesc from x$ksppi 2 where ksppinm like '%_resetlogs_%'; KSPPINM ---------------------------------------------------------------- KSPPDESC ---------------------------------------------------------------- _allow_resetlogs_corruption allow resetlogs even if it will cause corruption 該參數(shù)的含義是,允許在破壞一致性的情況下強制重置日志,打開數(shù)據(jù)庫。_allow_resetlogs_corruption將使用所有數(shù)據(jù)文件最舊的SCN打開數(shù)據(jù)庫,所以通常需要保證SYSTEM表空間擁有最舊的SCN。 丟失ACTIVE日志和CURRENT日志的情況下,如果沒有備份,只能使用隱含參數(shù)_allow_resetlogs_corruption強制啟動數(shù)據(jù)庫,設(shè)置此參數(shù)后,在數(shù)據(jù)庫open過程中,Oracle會跳過某些一致性檢查,從而使數(shù)據(jù)庫可能跳過不一致狀態(tài),直接打開。 Alter system set “_allow_resetlogs_corruption”=true scope=spfile; Recover database using backup controlfile until cancel; ----mount |
|