討論SQLite數據庫損壞與修復

昨晚,朋友和我反饋SQLite數據庫發生損壞有沒有辦法恢復。大致的情況是這樣的,當數據庫在使用時不小心用了新的文件覆蓋數據庫,導致了SQLite數據庫出現了損壞,打開的時候出現要輸入密碼,而且不能把SQL語句dump下來。所以,文章這裏整理SQLite數據庫出現損壞的所有情況,以及如何修復損壞的SQLite數據庫文件。

SQLite算是非常穩定的數據庫,不容易出現損壞,就算應用程序崩潰,或者操作系統崩潰,甚至是執行事務時出現斷電,都能在下一次使用數據庫時自動修復。但是,還是不能避免不出現損壞的情況。

導致SQLite數據庫損壞的情況

導致SQLite數據庫損壞的情況大致可歸結爲4類:文件覆蓋問題、文件鎖問題、數據同步問題、內存問題

文件覆蓋問題

SQLite數據庫文件被覆蓋是可能的,畢竟是一個普通的磁盤文件,意味着所有的進程都可以打開和覆蓋,所以不可能完全避免文件覆蓋的情況。
1. 多線程寫數據庫問題。
  SQLite數據庫是支持多進程併發讀寫,但是如果這時候關閉和重新打開數據庫,就很可能出現一些線程還在寫數據到數據庫,出現部分數據被覆蓋的情況。

2. 執行事務時備份或恢復數據
  事務都是一個過程性的操作,需要一定時間,而數據備份是原子操作,如果在事務執行過程時備份,可能導致複製的內容包含了部分新的內容和部分舊的內容,就出現數據庫損壞。恢復也是一樣。

3. 刪除日誌文件
  SQLite數據庫通常都是存儲所有內容到一個文件,但執行事務時,爲了實現程序崩潰,斷電時可以回滾日誌,就伴隨着一些附加的日誌文件。如果日誌被刪除了,就會導致恢復出現異常。

文件鎖問題

爲了實現SQLite數據庫併發讀寫,SQLite會使用文件鎖來保證數據安全。
1. 系統文件鎖問題
SQLite依賴於底層的文件系統對文件鎖的實現,但是,一些文件系統存在鎖邏輯錯誤,使得鎖並不可靠,這在網絡文件系統和NFS情況比較常見。

2. POSIX協同鎖(advisory lock)
在linux 或者unix下,SQLite 默認鎖是協同鎖。當進程使用協同鎖,如果其中有一個線程執行 close() 就可能導致鎖被取消。如果已經有兩個線程同時連接到同一個數據庫,再來一個線程不以SQLite API的形式,就是以系統文件形式讀取數據庫( open(),  read() ,  然後close()),就會導致這個進程的數據庫鎖被取消,而兩個線程同時操作數據庫就會導致數據覆蓋引起錯亂。

3. 不同的連接協議
不同的連接協議鎖也可能會不同,也就導致鎖沒有發揮錯誤引起錯誤。

4.當數據庫正在使用時刪除或重命名數據庫文件
出現這種情況往往是在linux等類POSIX系統,windows下不會出現這個情況,而且同時有事務執行就會放大這個問題。

數據同步問題

爲了保證數據一致性,SQLite有時候會請求操作系統將所有等待持久化的數據刷入磁盤,然後等待這個操作完成。
1.磁盤驅動器的同步請求可能是不可靠的
 現有普通消費級別的磁盤驅動器多數都會謊報數據同步結果,以期望得到更高的寫入速度。當數據剛到達磁盤緩衝區,還沒真正寫入氧化物介質,磁盤驅動器就報告內容已經安全寫入。但是這時候斷電、硬件復位就會導致數據同步失敗。這種情況主要出現在閃存介質。

2.使用PRAGMAs會影響同步
通過設置PRAGMA synchronous=OFF, SQLite所有的同步操作都會被忽略。這使得SQLite運行得更快,但如果出現電源故障或硬件復位就會前面保存的所有數據。如果單純爲了獲得最大的數據可靠性和健壯性,SQLite可設置synchronous = FULL

內存問題

SQLite作爲一個C運行庫,和使用它的應用程序運行在同一個內存地址空間。這意味着,任何野指針,緩衝區溢出,堆損壞等都有可能損壞了SQLite的數據結構,並最終導致數據庫文件損壞。
另外,使用內存映射I/O模型(如mmap)的時候,內存問題會變得更加嚴重。當數據庫文件的一部分或全部被映射到應用程序的地址空間,雖然減少了文件IO操作,但是野指針可能訪問並修改到任何部分的映射空間數據。

更多SQLite數據庫損壞的原因可以看這裏


修復損壞的SQLite數據庫

linux下:
$ sqlite3 mydata.db ".dump" | sqlite3 new.db
win下:
d:\>sqlite3 mydata.db .dump > mydata.sql
d:\>sqlite3 new.db < mydata.sql
d:\>sqlite3 aa.db "pragma integrity_check"

這裏可以下載 sqlite3.exe
當然,這些API只是在一定程序修復損壞的數據庫,無法解決所有的問題。

SQLite使用建議

這裏有4點建議:
1. 減少多進程或多線程操作,儘可能單線程寫。
2. 減少事務操作,減小事務複雜度,減少檢查點
3. 減少數據庫的大小
4. 避免使用PRAGMA synchronous=OFF

參考:http://blog.csdn.net/mycwq/article/details/45541409
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章