資料庫系統-學習記錄13
ch6.系統故障對策
資料庫在使用的過程中,難免會出現各種各樣的故障。這些故障往往會給資料帶來損失,這個時候就需要進行資料庫的恢復
可恢復操作的問題和模型
故障模式
在資料庫的使用中,最重要的故障包括錯誤資料輸入、介質故障、災難性故障和系統故障等。解決由系統錯誤引起的故障的基本方法,在於使用日誌,通過記錄每一條更新操作,以在必要時進行恢復
日誌管理器與事務管理器
一項事務只能是“全做”或“全不做”,事務管理器便是用於滿足這一規定。實際上,事務管理器的主要作用就是保證事務的正確執行,它的主要功能有:
1、通過給日誌管理器傳送訊號,讓日誌儲存相關的必要資訊
2、保證併發執行的事務不會相互干擾
而日誌管理器則是用於維護日誌。日誌的空間最早會出現在主存緩衝區中,而這些緩衝區又會在一定的時候被複制到磁碟上。日誌和資料一樣,都會佔用磁碟上的空間
下圖是日誌管理器與事務管理器的結構圖:
資料庫的一致性狀態
當資料庫的資料滿足資料庫模式所有約束時,稱資料庫處於一致性的狀態
由此引出事務的一個基本假設——正確性原則:如果事務在沒有其他任何事務和系統錯誤的情況下執行,並且在它開始執行時資料庫處於一致的狀態,那麼當事務結束時,資料庫仍然處於一致的狀態
事務的基礎(Primitive)操作
為了使後續描述方便簡潔,使用下述記法來描述所有資料在地址空間之間移動的操作:
- INPUT(X):將包含資料庫元素X的磁碟塊拷貝到主存緩衝區
- READ(X, t):將資料庫元素X拷貝到事務的區域性變數t。更準確地說,如果包含資料庫元素X的塊不在主存緩衝區中,則首先執行INPUT(X),接著將X的值賦給區域性變數t
- WRITE(X, t):將區域性變數t的值拷貝到主存緩衝區中的資料庫元素X。更準確地說,如果包含資料庫元素X的塊不在主存緩衝區中,則首先執行INPUT(X),接著將t的值拷貝到緩衝區中的X
- OUTPUT(X):將包含X的緩衝區中的塊拷貝回磁碟
現在,舉例說明上述幾種記法的使用方式:
假定有一個數據庫,只包含A、B兩個元素,這兩個元素需要滿足A=B的約束。即:滿足A=B的約束時,這個資料庫處於一致性狀態
現在,假定事務T在邏輯上包含下述兩步:
A := A*2;
B := B*2;
則T從一個一致的狀態(A=B)出發,在不受到干擾的情況下,到達的最終狀態必然也是一致的(根據之前提到的正確性原則)
假定初始時刻,A=B=8,則完成事務T的過程可以由下表表示:
動作 | t | 記憶體中的A | 記憶體中的B | 磁碟中的A | 磁碟中的B |
---|---|---|---|---|---|
READ(A, t) | 8 | 8 | 8 | 8 | |
t := t*2 | 16 | 8 | 8 | 8 | |
WRITE(A, t) | 16 | 16 | 8 | 8 | |
READ(B, t) | 8 | 16 | 8 | 8 | 8 |
t := t*2 | 16 | 16 | 8 | 8 | 8 |
WRITE(B, t) | 16 | 16 | 16 | 8 | 8 |
OUTPUT(A) | 16 | 16 | 16 | 16 | 8 |
OUTPUT(B) | 16 | 16 | 16 | 16 | 16 |
只有在執行OUTPUT(A)和OUTPUT(B)這兩步之間發生故障時,才會影響到磁碟中的值的一致性。如果在這樣的地方出現了故障,恢復的時候就應該考慮讓A和B都等於16,或者都還原為8
undo日誌
日誌記錄構成的檔案,稱為日誌。日誌有多種型別,其中,undo日誌通過撤銷事務在系統崩潰前可能還沒有完成的影響來修復資料庫狀態
日誌記錄
日誌可以被看做一個按只允許附加的方式開啟的檔案
各種日誌型別所用到的日誌記錄包括:
1、<START T>:表示事務T開始
2、<COMMIT T>:表示事務T已成功完成,並且對資料庫元素不會再進行修改
3、<ABORT T>:表示事務T不能成功完成
對於undo日誌而言,還需要的日誌記錄型別是更新記錄:<T, X, v>,此三元組的含義為:事務T改變了資料庫元素X,而X原來的值為v。更新記錄所反映的改變通常都是在主存中進行的,即都與WRITE有關(而不是OUTPUT)
undo日誌不記錄新值,只記錄舊值。在恢復時,是通過還原舊值來進行的
undo日誌規則
為了讓undo日誌能夠保證從系統故障中恢復,與一個事務相關的內容應該按如下順序寫入到磁碟:
1、指出“改變資料庫元素”的日誌記錄
2、對資料庫元素進行修改
3、COMMIT日誌記錄
這樣,按undo日誌來考慮之前提到的那個事務(初始時刻A=B=8)時,就可以在進行各動作時,寫入相應的日誌記錄:
從表中可以看出,START出現在所有動作之前,COMMIT出現在OUTPUT之後,而更新記錄則分別出現在兩個WRITE處。其中,步驟8表示重新整理日誌,這是為了保證這些日誌記錄出現在磁碟上;而步驟12則是單獨保證COMMIT能夠出現在磁碟上
使用undo日誌的恢復
在事務執行到一半時發生故障的情況下,事務的原子性將無法得到保證。此時,恢復管理器會使用日誌,將資料庫恢復到某個一致的狀態
為方便討論,此處先假定恢復管理器會檢視整個日誌的全部內容。如果檢視到了日誌記錄<COMMIT T>,則說明事務T帶來的全部改變已經寫到了磁碟上,即事務T不需要被恢復管理器考慮
而如果只看到事務T的<START T>,卻沒有看到<COMMIT T>,則說明事務T執行到中途的時候出現了故障,以至於沒有執行完成。此時,就應該讓恢復管理器來進行相應的處理了。事務T對磁碟的的每一項改變,都有<T, X, v>這樣的三元組來記錄,因此,想要進行恢復,讓X的值被還原成舊值v,也是易如反掌的事
在進行了恢復操作後,需要再往日誌中新增<ABORT T>,以表示事務T不能成功完成。處理結束後,可以恢復資料庫的正常操作
需要注意的是,可能有多個未完成的事務,並且這些事務對同一個值X進行了多次修改。所以從尾部開始往上掃描是非常重要的,這樣可以保證按順序恢復
檢查點
前面提及恢復管理器的運作時,假設了恢復管理器會檢視整個日誌的全部內容。但這顯然是不現實的:隨著日誌內容的增加,恢復管理器檢視日誌的耗時會越來越長
如何解決這樣的問題?首先考慮在接收到COMMIT日誌記錄時刪除整個日誌的方法,這是行不通的,因為在接收到一個事務T的COMMIT記錄時,另一個事務T’可能正在執行,如果直接刪除,T’的記錄就會丟失,變得不完整
而週期性地對日誌做檢查點則可以很好地解決這個問題,在一個檢查點中:
- 停止接收新的事務
- 等到所有當前活躍的事務提交或中止並且在日誌中寫人了 COMMIT或ABORT記錄
- 將日誌重新整理到磁碟
- 寫入日誌記錄<CKPT>,並再次重新整理日誌
- 重新開始接收事務
這樣一來,每次從日誌尾部往前走時,只要在碰到<CKPT>標記時停下,就可以避免整個日誌都讀一遍了。這是合理的,因為<CKPT>之前的所有事務都已經執行完成了
非靜止檢查點
靜止檢查點有一個缺陷,就是等待活躍事務結束的這個過程可能會很長,這樣一來,系統就會有很長一段時間處於不工作的階段。因此,非靜止檢查點作為不需要等待的方法,是更受歡迎的
非靜止檢查點使用<START CKPT(T1, …, Tn)>來表示檢查點的開始,其中T1,…,Tn表示在設定非靜止點的開始位置時,正在執行的活躍事務。待這些活躍事務執行完後,再使用<END CKPT>來表示檢查點的結束。期間依舊允許接收其他事務,這樣一來就可以在檢查的同時又能夠照常運作
對於非靜止檢查點而言,從日誌尾部往前走時,如果先遇到了<END CKPT>,便說明在檢查點開始時仍在執行的那些事務T1,…,Tn已經成功完成了,目前的活躍事務一定是在<START CKPT(T1, …, Tn)>之後開始的。所以,在這種情況下,只要從<START CKPT(T1, …, Tn)>開始檢查就可以了
而如果先遇到的是START記錄,則說明在設立檢查點期間出現了故障。這樣的話,可以知道的是:未執行完成的事務一定出現在T1,…,Tn之間。這種情況下,只需從這些事務中·開始得最早的那個的START記錄·開始檢查即可
redo日誌
undo日誌有一個缺點,就是在COMMIT之後才能真正地輸入新值,否則在中間發生故障就一定得“打回原點”(恢復舊值)。如果讓資料庫的修改暫時只存在於主存中,那麼就可以節省磁碟I/O
與undo日誌相反,使用redo日誌恢復時,恢復的是新值(而不會打回原點)
redo日誌規則
與undo日誌不同,redo日誌中的三元組<T, X, v>表示“事務T為資料庫元素X寫入新值v”,每當事務T修改一個元素X後,必須往日誌中寫入這樣的記錄
在redo日誌規則下,與一個事務相關的內容應該按如下順序寫入到磁碟:
1、指出“改變資料庫元素”的日誌記錄
2、COMMIT日誌記錄
3、改變資料庫元素自身
繼續以前述資料庫(A=B=8)為例,其動作與日誌項對應的redo版本為:
注意到與undo版本不同的地方:COMMIT不再是在OUTPUT之後,而是到了OUTPUT之前
使用redo日誌的恢復
使用redo日誌恢復的大致流程如下:
- 確定已提交的事務
- 從首部開始掃描日誌。對遇到的每一 <T, X, v>記錄:
a) 如果T是未提交的事務,則什麼也不做
b) 如果T是提交的事務,則為資料庫元素X寫入值v; - 對每個未完成的事務T,在日誌中寫人一個< ABORT T>記錄並重新整理日誌
如果有START沒有COMMIT,則肯定是可以保證磁碟沒有被修改的,這個時候就可以完全不管。但如果已經COMMIT過了的話,則可能會存在磁碟只被修改了一半的情形,這個時候,以防萬一,就需要將事務中涉及到的資料X恢復為新值v
redo日誌的檢查點
與undo不同,redo日誌的檢查點需要考慮髒緩衝區。因為COMMIT後並不能確認是否完整地將資料寫入了磁碟,所以在檢查點的開始和結束之間必須將已被提交事務修改的所有資料庫元素寫入到磁碟
redo日誌檢查時不需要等待活躍事務提交或中止就能完成檢查點,redo日誌的非靜止檢查點步驟如下:
- 寫入日誌記錄<START T1,…,Tk>,其中T1,…,Tn是所有活躍(即未提交的)事務,並重新整理日誌
- 將<START CKPT>記錄寫入日誌時所有已提交事務已經寫到緩衝區但還沒有寫到磁碟的資料庫元素寫到磁碟。
- 寫入日誌記錄<END CKPT>並重新整理日誌
使用帶檢查點redo日誌的恢復
由於在START CKPT時不能確定在這之前提交併已經COMMIT的事務是否完成了磁碟寫入,因此需要定位到上一個END CKPT對應的START CKPT處開始檢查
undo/redo日誌
前面所提到的undo日誌和redo日誌,都各有缺陷:前者具有更多磁碟I/O數,後者可能使用更多平均緩衝區數
而undo/redo日誌則是以在日誌中維護更多資訊為代價,提供動作順序上的更大的靈活性
undo/redo規則
undo/redo規則,在資料庫元素修改值的時候,寫入的更新日誌記錄會是一個四元組:<T, X, v, w>,含義為“事務T改變了資料庫X的值,將v改變為w”
undo/redo日誌系統必須遵從的約束:在由於某個事務T所做改變而修改磁碟上的資料庫元素X前,更新記錄<T, X, v, w>必須出現在磁碟上
在undo/redo規則下,COMMIT可以出現在寫入磁碟前,也可以出現在寫入磁碟後,還可以出現在幾次寫入磁碟操作的中間
再次回到之前的例子(A=B=8)中,日誌的日誌項的一個可能序列為:
使用undo/redo日誌的恢復
undo/redo日誌恢復的策略:
- 按照從前往後做順序,重做所有已提交的事務
- 按照從後往前做順序,撤銷所有未提交的事務
注意到在undo/redo日誌下,undo和redo這兩種日誌的恢復策略被結合了起來
undo/redo日誌的檢查點
undo/redo日誌的非靜止檢查點:
- 寫人日誌記錄<START CKPT(T1,…,Tk)>,其中T1,…,Tk是所有的活躍事務,並重新整理日誌
- 將所有髒緩衝區寫到磁碟,髒緩衝區即包含一個或多個修改過的資料庫元素的緩衝區。和redo日誌不同的是,我們重新整理所有的髒緩衝區,而不是僅重新整理那些被提交事務寫過的緩衝區
- 寫入日誌記錄<END CKPT>並重新整理日誌
針對介質故障的防護
備份
備份:維護與自身分離的一個數據庫拷貝
在利用備份進行恢復時,可以使用日誌恢復到最近狀態