在停電的情況下依靠 Firebird 2.5 Embedded DB 中的事務是否安全?
在送出事務之前,如果 #2 行中間某處斷電,那麼如何可以肯定地說以下操作列表將永遠不會反映在 DB 中?
#1 begin transaction #2 delete record in table A that cascade deletes another record in table B #3 update another record in table C #4 commit transaction
這通常是安全的。Firebird 使用 MVCC(多版本並發控制)架構。更新(和刪除)被寫為一個備份版本和一個新版本。新版本會覆蓋以前的版本,而後面的版本是從新記錄版本重新創建以前記錄版本的增量。刪除被寫為“存根”記錄版本,將記錄標記為已刪除。
每個版本都標有最初創建記錄版本的事務。如果該事務未送出,則該版本的記錄將不可見。Firebird 將沿著記錄的反向版本指針鏈找到對事務可見的版本並重建該版本。
在崩潰或其他突然終止之後,Firebird 會在某個時候檢測到事務未處於活動狀態,並將事務標記為回滾。在那個時候,或者在垃圾回收過程中的某個時間點,Firebird 將重寫記錄版本,使最新送出的版本成為“新”版本,並消除不必要的回溯版本。
只要單個記錄的一致性可以,您在範例中具有級聯刪除的事實並不真正相關。
如果事務異常退出,如何從 Active 變為 Rolled Back?
這可以通過以下兩種方式之一發生。
- 當一個事務開始時,它會在自己的事務 id 上取一個鎖。如果事務 (B) 嘗試更新或刪除記錄,並發現記錄的最新版本是由 TIP 狀態為 ACTIVE 的事務 (A) 創建的,則事務 B 嘗試獲取 A 的事務 id 上的衝突鎖。實時事務在其自己的 id 上維護一個獨占鎖,鎖管理器可以探測鎖以查看所有者是否還活著。如果授予鎖,則 B 知道 A 已死亡,並將 A 的 TIP 狀態從 Active 更改為 Rolled Back。
- 當一個事務開始時,它會檢查它是否可以在數據庫上獲得一個排他鎖——如果它可以沒有其他事務處於活動狀態。每個活動事務在數據庫上都有一個共享鎖。如果它獲得排他鎖,它會將所有活動 TIP 條目轉換為回滾。
(來自:數據庫專家的 Firebird:第 4 集 - OAT、OIT 和 Sweep)
為了防止數據失去,Firebird 採用所謂的“小心寫入”策略來確保磁碟上的一致性,通過確保數據寫入的順序保持數據頁(和版本)之間的依賴關係。
新記錄寫入與舊記錄相同的位置,而寫入回版本以重建舊記錄。基本上有兩種情況:
- 新版本及其後版本適契約一數據頁。
新版本和舊版本都寫入頁面的記憶體映像,然後將頁面寫入磁碟。
在這種情況下,唯一真正的“故障”模式是磁碟寫入本身在電源故障時僅部分完成。這個問題可以通過使用帶有備用電池單元 (BBU) 的磁碟控制器或類似解決方案來解決。 2. 背面版本不適契約一頁面,必須轉到另一個數據頁面。
後置版本和新版本寫入各自頁面的記憶體映像中。由於新版本依賴於後版本(它指向後版本),因此先將具有後版本的頁面寫入磁碟,然後將具有新版本的頁面寫入磁碟。
如果在寫入後版本後程序終止,則後版本是孤立的,但前一版本的記錄仍然完好無損。它也具有與前一個相同的故障模式。
因此,在斷電或其他類型的 Firebird 程序終止的情況下,這意味著您可能有孤立頁面(已分配但尚未註冊的頁面,或已釋放但尚未標記為可用的頁面)或孤立記錄(已編寫但尚未在版本記錄鏈中正確連結的回溯版本)。這些孤立頁面或返回版本會浪費空間,但不會影響一致性。可能有必要使用 gfix 來修復和回收以這種方式浪費的空間。
也就是說,除了在寫入過程中出現故障和沒有 BBU 之外,我相信在某些情況下,斷電會使數據庫處於首先需要使用 gfix 修復的狀態,但我自己沒有遇到過這些情況,我可以現在還真沒想到。
這一切都假定您在 Firebird 數據庫(預設)上啟用了同步寫入(強制寫入)。如果您禁用同步寫入,則將頁面寫入磁碟將留給您的作業系統,這可能會導致數據失去,因為頁面可能以不同的順序寫入,例如新版本已寫入磁碟,但返回的版本在中斷的時候還沒有,那麼你基本上已經失去了行的送出版本,孤立了以前的版本,並且新版本的後版本指針指向垃圾。
進一步閱讀: