Sqlite Database是一個比較穩定輕量的小型數據庫,不像是mysql、oracle等數據庫同樣具備單獨的服務進程。基於sqlite的讀寫都是讀寫原始的磁盤文件,也就是說sqlite的操做徹底是磁盤的IO操做。SQLite操做存在必定的異常狀況,在客戶端也難以監控,在用戶基數比較大的狀況下,每每對極少數的用戶的影響也是致命的。瞭解sqlite便於咱們規範的去使用它。html
一般的異常狀況都包含在如下狀況:mysql
官方的文檔也有介紹到android
How To Corrupt An SQLite Database File sql
鑑於操做系統和Sqlite bug等不可控方面咱們更多須要作到能準確的監控,如內存不足,相似於微信的作法就是監控到內存不足,彈出提示框告知用戶去清理內存。下面就來探討下因爲操做不當引發的異常。數據庫
android.database.sqlite.SQLiteDatabaseLockedException DB引擎在執行job的時候發現獲取不到數據庫的鎖就會拋出這個異常。數據庫鎖用於防止多線程同時寫入數據從而致使DB損壞。因此通常發生在多線程的事務操做上。針對同一個事務代碼段,若是一個事務還沒有結束提交,另一個線程便開始介入執行就會致使衝突拋出此異常。微信
因此出現上述異常,請檢查你的代碼。建議的操做是:多線程
系統文件鎖問題SQLite依賴於底層的文件系統對文件鎖的實現。SQLite 默認鎖是協同鎖。假設有A、B線程同時訪問數據庫並寫入數據。這個時候來個C線程不是使用SQLite API的方式來操做數據的話(如直接的IO操做),那麼數據庫鎖就會被自動取消,A、B線程在沒有鎖保護的狀態同時操做db就可能致使DB的損壞。oracle
建議:性能
批量寫入比較大的數據咱們可能想要更快的速度,網上不少會建議你設置PRAGMA synchronous=OFF, Android對應的設置方式就是:mDatabase.execSQL("PRAGMA synchronous=OFF"); 等同於在說:我期待更快的寫入速度,磁盤驅動器爲了達到目標,就耍了個小聰明,忽略開始SQLite的同步操做,先通知到系統我寫入完了。實際上不必定數據徹底寫入了,假如這個時候出現了斷電等意外,就會致使真實數據寫入失敗從而引起DB損壞。優化
建議:
儘可能不要設置PRAGMA synchronous=OFF 數據比較大的寫入等操做從sql語句優化和性能的優化入手。
不少較爲複雜的應用可能一個應用程序不止有一個數據庫。這裏列舉一個場景,應用包含兩個DB。 DatabaseA:預置數據的數據庫,已經設計好表並提早插入了數據。不須要跟隨應用動態的去建立DB。用戶第一次安裝的時候把準備好的DB經過IO的方式拷貝到應用包路徑下面的databases路徑提供給應用訪問,此DB通常只用讀,不多或者徹底不寫入數據,是靜態的。 DatabaseB: 跟隨應用的安裝和升級動態的建立和升級,做爲應用數據的持久化方式之一。讀寫操做均比較頻繁。
DababaseA會在應用的啓動的時候IO遷移拷貝。DatabaseB動態Create後處處可能都有使用到(包括啓動)
Danger 1:文件覆蓋
DatabaseB的DBOpenHelperB正在讀寫DatabaseA的表,同時DatabaseA正在被IO線程拷貝寫入文件覆蓋。DBOpenHelperB創建鏈接可能依然是舊的數據庫,讀寫天然也是到舊的數據庫。部分文件系統甚至引起IO拷貝被中斷。
Danger 2:交叉讀寫
通常有多個DB會對應多個DBOpenHelper進行讀寫,如過DBOpenHelper作了跨DB的讀寫,就會致使重複的DB Open或Close,影響到了其餘DBOpenHelper的正常讀寫。直接的致使鎖異常或者DB損壞等問題。
多DB建議:
綜上多數狀況都是代碼上操做不當引起的問題,瞭解好原理才能對症下藥,避開危險區。
補充: 減小多進程或多線程操做,儘量單線程寫; 減小事務操做,減少事務複雜度。