學習《Oracle 9i10g編程藝術》的筆記 (十二) redo 和undo 如何協作

作爲一個例子,我們將分析對於下面這組語句可能發生什麼情況:

insert into t (x,y) values (1,1);
update t set x = x+1 where x = 1;
delete from t where x = 2;


我們會沿着不同的路徑完成這個事務,從而得到以下問題的答案:
     如果系統在處理這些語句的不同時間點上失敗,會發生什麼情況?
     如果在某個時間點上ROLLBACK,會發生什麼情況?
     如果成功並COMMIT,會發生什麼情況?


1. INSERT
對於第一條INSERT INTO T 語句,redo 和undo 都會生成。所生成的undo 信息足以使INSERT“消失
“。INSERT INTO T 生成的redo 信息則足以讓這個插入”再次發生“。
插入發生後,系統狀態如圖9-1 所示。


這裏緩存了一些已修改的undo 塊、索引塊和表數據塊。這些塊得到重做日誌緩衝區中相應條目的“保
護“

假想場景:系統現在崩潰
即使系統現在崩潰也沒有關係。SGA 會被清空,但是我們並不需要SGA 裏的任何內容。重啓動時就好
像這個事務從來沒有發生過一樣。沒有將任何已修改的塊刷新輸出到磁盤,也沒有任何redo 刷新輸出到磁
盤。我們不需要這些undo 或redo 信息來實現實例失敗恢復。
假想場景:緩衝區緩存現在已滿
在這種情況下,DBWR 必須留出空間,要把已修改的塊從緩存刷新輸出。如果是這樣,DBWR 首先要求
LGWR 將保護這些數據庫塊的redo 條目刷新輸出。DBWR 將任何有修改的塊寫至磁盤之前,LGWR 必須先刷新
輸出與這些塊相關的redo 信息。這是有道理的——如果我們要刷新輸出表T 中已修改的塊,但沒有刷新輸
出與undo 塊關聯的redo 條目,倘若系統失敗了,此時就會有一個已修改的表T 塊,而沒有與之相關的redo
信息。在寫出這些塊之前需要先刷新輸出重做日誌緩存區,這樣就能重做(重做)所有必要的修改,將SGA
放回到現在的狀態,從而能發生回滾。
從第二個場景還可以預見到一些情況。這裏描述的條件是“如果刷新輸出了表T 的塊,但沒有刷新輸
出undo 塊的相應redo,而且此時系統失敗了“,這個條件開始變得有些複雜。隨着增加更多用戶、更多
的對象,再加上併發處理等因素,條件還會更復雜。
此時的情況如圖9-1 所示。我們生成了一些已修改的表和索引塊。這些塊有一些與之關聯的undo 段
塊,這3 類塊都會生成redo 來保護自己。如果還記得第4 章中對重做日誌緩衝區的討論,應該知道,它會
在以下情況刷新輸出:每3 秒一次;緩衝區1/3 滿時或者包含了1MB 的緩衝數據;或者是隻要發生提交就
會刷新輸出。重做日誌緩衝區還有可能會在處理期間的某一點上刷新輸出。在這種情況下,其狀態如圖9-2
所示。

2. UPDATE
UPDATE 所帶來的工作與INSERT 大體一樣。不過UPDATE 生成的undo 量更大;由於存在更新,所以需
要保存一些“前“映像。系統狀態如圖9-3 所示。

塊緩衝區緩存中會有更多新的undo 段塊。爲了撤銷這個更新,如果必要,已修改的數據庫表和索引
塊也會放在緩存中。我們還生成了更多的重做日誌緩存區條目。下面假設前面的插入語句生成了一些重做
日誌,其中有些重做日誌已經刷新輸出到磁盤上,有些還放在緩存中。

假想場景:系統現在崩潰
啓動時,Oracle 會讀取重做日誌,發現針對這個事務的一些重做日誌條目。給定系統的當前狀態,
利用重做日誌文件中對應插入的redo 條目,並利用仍在緩衝區中對應插入的redo 信息,Oracle 會“前滾”
插入。最後到與圖9-1 類似的狀態。現在有一些undo 塊(用以撤銷插入)、已修改的表塊(剛插入後的狀
態),以及已修改的索引塊(剛插入後的狀態)。由於系統正在進行崩潰恢復,而且我們的會話還不再連接
( 這是當然),Oracle 發現這個事務從未提交,因此會將其回滾。它取剛剛在緩衝區緩存中前滾得到的undo,
並將這些undo 應用到數據和索引塊,使數據和索引塊“恢復”爲插入發生前的樣子。現在一切都回到從前。
磁盤上的塊可能會反映前面的INSERT,也可能不反映(這取決於在崩潰前是否已經將塊刷新輸出)。如果
磁盤上的塊確實反映了插入,而實際上現在插入已經被撤銷,當從緩衝區緩存刷新輸出塊時,數據文件就
會反映出插入已撤銷。如果磁盤上的塊本來就沒有反映前面的插入,就不用去管它——這些塊以後肯定會
被覆蓋。
這個場景涵蓋了崩潰恢復的基本細節。系統將其作爲一個兩步的過程來完成。首先前滾,把系統放到
失敗點上,然後回滾尚未提交的所有工作。這個動作會再次同步數據文件。它會重放已經進行的工作,並
撤銷尚未完成的所有工作。

假想場景:應用回滾事務
此時,Oracle 會發現這個事務的undo 信息可能在緩存的undo 段塊中(基本上是這樣),也可能已經
刷新輸出到磁盤上(對於非常大的事務,就往往是這種情況)。它會把undo 信息應用到緩衝區緩存中的數
據和索引塊上,或者倘若數據和索引塊已經不在緩存中,則要從磁盤將數據和索引塊讀入緩存,再對其應
用undo。這些塊會恢復爲其原來的行值,並刷新輸出到數據文件。
這個場景比系統崩潰更常見。需要指出,有一點很有用:回滾過程中從不涉及重做日誌。只有恢復和
歸檔時會當前重做日誌。這對於調優是一個很重要的概念:重做日誌是用來寫的(而不是用於讀)。Oracle
不會在正常的處理中讀取重做日誌。只要你有足夠的設備,使得ARCH 讀文件時,LGWR 能寫到另一個不同
的設備,那麼就不存在重做日誌競爭。許多其他的數據庫(非Oracle)都把日誌文件處理爲“事務日誌”。
這些數據庫沒有把redo 和undo 分開。對於這些系統,回滾可能是災難性的,回滾進程必須讀取日誌,而
日誌寫入器正在試圖寫這個日誌。這就向系統中最薄弱的環節引入了競爭。Oracle 的目標是:可以順序地
寫日誌,而且在寫日誌時別人不會讀日誌。

3. DELETE
同樣,DELETE 會生成undo,塊將被修改,並把redo 發送到重做日誌緩衝區。這與前面沒有太大的不
同。實際上,它與UPDATE 如此類似,所以我們不再囉嗦,直接來介紹COMMIT。

4. COMMIT
我們已經看到了多種失敗場景和不同的路徑,現在終於到COMMIT 了。在此,Oracle 會把重做日誌緩
衝區刷新輸出到磁盤,系統狀態如圖9-4 所示。

已修改的塊放在緩衝區緩存中;可能有一些塊已經刷新輸出到磁盤上。重做這個事務所需的全部redo
都安全地存放在磁盤上,現在修改已經是永久的了。如果從數據文件直接讀取數據,可能會看到塊還是事
務發生前的樣子,因爲很有可能DBWR 還沒有(從緩衝區緩存)寫出這些塊。這沒有關係,如果出現失敗,
可以利用重做日誌文件來得到最新的塊。undo 信息會一直存在,除非undo 段迴繞重用這些undo 塊。如果
某些對象受到影響,Oracle 會使用這個undo 信息爲需要這些對象的會話提供對象的一致讀。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章