深刻SQLite,一網打盡「危險操做」

簡述

Sqlite Database是一個比較穩定輕量的小型數據庫,不像是mysql、oracle等數據庫同樣具備單獨的服務進程。基於sqlite的讀寫都是讀寫原始的磁盤文件,也就是說sqlite的操做徹底是磁盤的IO操做。SQLite操做存在必定的異常狀況,在客戶端也難以監控,在用戶基數比較大的狀況下,每每對極少數的用戶的影響也是致命的。瞭解sqlite便於咱們規範的去使用它。html

一般的異常狀況都包含在如下狀況:mysql

  • 文件錯寫
  • 文件鎖 bug
  • 文件 sync 失敗
  • 設備損壞
  • 內存覆蓋
  • 操做系統 bug
  • SQLite bug

官方的文檔也有介紹到android

How To Corrupt An SQLite Database File sql

鑑於操做系統和Sqlite bug等不可控方面咱們更多須要作到能準確的監控,如內存不足,相似於微信的作法就是監控到內存不足,彈出提示框告知用戶去清理內存。下面就來探討下因爲操做不當引發的異常。數據庫

SQLiteDatabaseLockedException

android.database.sqlite.SQLiteDatabaseLockedException DB引擎在執行job的時候發現獲取不到數據庫的鎖就會拋出這個異常。數據庫鎖用於防止多線程同時寫入數據從而致使DB損壞。因此通常發生在多線程的事務操做上。針對同一個事務代碼段,若是一個事務還沒有結束提交,另一個線程便開始介入執行就會致使衝突拋出此異常。微信

因此出現上述異常,請檢查你的代碼。建議的操做是:多線程

  1. 只使用一個SQLOpenHelper來訪問和操做database,單例你的SQLOpenHelper,避免多個OpenHelper屢次open db並寫入數據。
  2. 當再也不須要繼續操做db的時候,關閉全部database helper實例
  3. 作事務操做的時候必定要保證事務完成調用了endTransaction() 或者transaction Successful相關方法。下一個事務操做會在entransaction結束後請求鎖。

文件鎖

系統文件鎖問題SQLite依賴於底層的文件系統對文件鎖的實現。SQLite 默認鎖是協同鎖。假設有A、B線程同時訪問數據庫並寫入數據。這個時候來個C線程不是使用SQLite API的方式來操做數據的話(如直接的IO操做),那麼數據庫鎖就會被自動取消,A、B線程在沒有鎖保護的狀態同時操做db就可能致使DB的損壞。oracle

建議:性能

  1. 儘可能使用SQLite API來操做數據庫的讀寫。
  2. 若是有IO線程直接操做數據庫,代碼邏輯上保證線程間的同步順序。

文件sync失敗

批量寫入比較大的數據咱們可能想要更快的速度,網上不少會建議你設置PRAGMA synchronous=OFF, Android對應的設置方式就是:mDatabase.execSQL("PRAGMA synchronous=OFF"); 等同於在說:我期待更快的寫入速度,磁盤驅動器爲了達到目標,就耍了個小聰明,忽略開始SQLite的同步操做,先通知到系統我寫入完了。實際上不必定數據徹底寫入了,假如這個時候出現了斷電等意外,就會致使真實數據寫入失敗從而引起DB損壞。優化

建議:

儘可能不要設置PRAGMA synchronous=OFF 數據比較大的寫入等操做從sql語句優化和性能的優化入手。

多db操做

不少較爲複雜的應用可能一個應用程序不止有一個數據庫。這裏列舉一個場景,應用包含兩個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建議:

  1. 非SQLite API線程IO數據庫請保持IO徹底結束後再創建與數據庫的讀寫鏈接或讀寫操做 管理好你的DBHelper
  2. DBHelper避免跨DB的操做,一個DB對應一個DBHelper實例

綜上多數狀況都是代碼上操做不當引起的問題,瞭解好原理才能對症下藥,避開危險區

補充: 減小多進程或多線程操做,儘量單線程寫; 減小事務操做,減少事務複雜度。

相關文章
相關標籤/搜索