小。快。可靠。
任選三項。

Powersafe 覆寫

「Powersafe 覆寫」是 SQLite 團隊用來描述某些檔案系統和磁碟控制器在斷電期間資料保存相關行為的術語。Powersafe 覆寫是一個布林屬性:儲存系統有或沒有它。

如果下列陳述為真,我們會說系統具有 Powersafe 覆寫屬性

當應用程式在檔案中寫入一個位元組範圍時,即使寫入發生在崩潰或斷電之前,該範圍外的位元組也不會改變。

Powersafe 覆寫屬性沒有說明已寫入位元組的狀態。這些位元組可能包含它們的舊值、新值、隨機值或這些值的某種組合。Powersafe 覆寫屬性僅表示寫入無法改變寫入位元組範圍外的位元組。

換句話說,Powersafe 覆寫表示在寫入時發生斷電時沒有「附帶損壞」。只有實際正在寫入的那些位元組可能會損壞。

實際上,磁碟控制器偵測到即將斷電時,Powersafe 寫入屬性的意思是它會完成處理中的任何扇區寫入,然後再停放磁頭。這表示一旦開始個別扇區寫入,即使斷電,也會完成寫入。

考慮一下如果磁碟區塊寫入因斷電而中斷會發生什麼事。如果應用程式在某個檔案的中間寫入兩個或三個位元組,作業系統會先讀取包含那些位元組的整個區塊,在記憶體中對區塊進行變更,然後將整個區塊寫回磁碟來執行此動作。如果在寫回期間發生斷電,而且區塊尚未完全寫入,則在重新開機後的下一次讀取時,區塊中的錯誤修正碼可能會偵測到無法修復的損壞,而且磁碟控制器會將區塊讀出為全零或全一。因此,值會在應用程式層級寫入的兩個或三個位元組範圍之外變更,這會違反電力安全覆寫屬性。

SQLite 關於電力安全覆寫的假設

所有版本到 3.7.9 版 (2011-11-01) 為止的 SQLite 都假設檔案系統提供電力安全覆寫。SQLite 傳統上假設當檔案的任何一個位元組變更時,同一區塊中的所有其他位元組都有可能在斷電時損毀。在寫入時,SQLite 已確保記錄任何修改的同一區塊中的所有位元組,而且將記錄檔填補到下一個區塊邊界,以便後續附加到該記錄檔不會損壞先前的記錄。SQLite 了解區塊大小是 VFS 中 xSectorSize 方法傳回的值。SQLite 團隊經常將 xSectorSize 傳回的值稱為寫入的「爆炸半徑」,因為它表示如果在寫入期間發生斷電,可能會損壞的位元組範圍。預設的 unix 和 windows VFS 始終將 512 傳回為所有版本到 3.7.9 版為止的 SQLite 的區塊大小 (或爆炸半徑)。

然而,較新的磁碟機已開始使用 4096 位元組扇區。從 SQLite 版本 3.7.10(2012 年 1 月 16 日)開始,SQLite 開發團隊嘗試將 xSectorSize 變更為回報 4096 位元組作為爆破半徑。這會增加許多資料庫的寫入負擔。對於 PRAGMA page_size 為 1024(非常常見的選擇)的資料庫,現在對資料庫中的單一頁面進行變更,需要 SQLite 將其他三個相鄰頁面備份到回滾記錄檔,而以前只需要備份正在變更的那個頁面。在 WAL 模式 中,每個交易都必須填補到 WAL 檔案中的下一個 4096 位元組邊界,而不是下一個 512 位元組邊界,導致每個交易寫入數千個額外的位元組。

額外的寫入負擔促使重新檢視關於電源安全覆寫的假設。對於現代磁碟機,容量已變得如此龐大,資料密度也如此之高,以至於單一扇區非常小,而寫入單一扇區只需很短的時間。我們知道磁碟機可以偵測即將來臨的斷電,並繼續使用殘餘能量運作一小段時間,因為這些磁碟機可以在關機前將磁頭歸位。因此,如果磁碟控制器可以偵測到即將來臨的斷電,那麼在偵測到即將來臨的斷電時,控制器會在將磁頭歸位之前完成寫入它目前正在處理的任何扇區,只要這樣做不會花費太長的時間,而對於小而密集的扇區來說,這應該不會花費太長的時間。因此,對於現代磁碟來說,假設電源安全覆寫似乎是合理的。據說,BerkeleyDB 確實已對此假設進行了數十年的驗證。不過建議保持謹慎。正如 Roger Binns 在 SQLite 開發人員郵件清單中所指出的:「『寫得很差』應該是磁碟機韌體的主要假設。」

撕裂頁面

當資料庫頁面大於磁碟區塊時,會產生一個撕裂的頁面,資料庫頁面會寫入磁碟,但在資料庫頁面的所有區塊寫入之前發生電源中斷。然後,在復原時,資料庫頁面的一部分會有舊內容,而頁面的其他部分則會有新內容。有些資料庫引擎假設頁面寫入是原子性的,因此撕裂的頁面是無法復原的錯誤。

SQLite 永遠不會假設資料庫頁面寫入是原子性的,不論 PSOW 設定為何。(1) 因此,SQLite 總是能夠自動從因崩潰而產生的撕裂頁面復原。啟用 PSOW 也不會降低 SQLite 從撕裂頁面復原的能力。

SQLite 版本 3.7.10 的變更

SQLite 版本 3.7.10 (2012-01-16) 的 VFS 新增了一個名為 SQLITE_IOCAP_POWERSAFE_OVERWRITE 的新裝置特性。報告此特性的資料庫檔案假設存在於具有電源安全覆寫特性的儲存系統上。如果 SQLite 是使用 -DSQLITE_POWERSAFE_OVERWRITE=1 編譯,預設的 unix 和 windows VFS 現在會報告 SQLITE_IOCAP_POWERSAFE_OVERWRITE;如果使用 -DSQLITE_POWERSAFE_OVERWRITE=0 編譯,它們會做出儲存沒有電源安全覆寫特性的舊假設。目前,預設是開啟電源安全覆寫,不過我們可能會在未來重新檢視這一點並將其預設為關閉。

可以在使用「psow」查詢參數和 URI 檔名 開啟資料庫時,將個別資料庫的電源安全覆寫特性指定為資料庫。例如,要始終假設檔案的電源安全覆寫(可能是為了確保最大的寫入效能),請將其開啟為

檔案:somefile.db?psow=1

或為了資料庫更安全,並強制 SQLite 假設資料庫缺乏 power-safe 覆寫,請使用下列方式開啟資料庫

檔案:somefile.db?psow=0

還有一個新的 SQLITE_FCNTL_POWERSAFE_OVERWRITE opcode 可用於 sqlite3_file_control(),讓應用程式可以查詢資料庫檔案的 power-safe 覆寫屬性。


備註

  1. SQLite 永遠不會假設原子頁面寫入在其預設組態中。但自訂 VFS 可以設定 SQLITE_IOCAP_ATOMIC 位元之一在 xDeviceCharacteristic() 方法的結果中,然後 SQLite 將假設頁面寫入是原子的。不過,應用程式必須提供自訂 VFS 來完成此操作,因為沒有任何標準 VFS 會在 xDeviceCharacteristics() 向量中設定任何原子位元。

此頁面最後修改於 2022-01-08 05:02:57 UTC