SQLite 線程安全和併發

SQLite 與線程

SQLite 是線程安全的。

線程模型

SQLite 支持如下三種線程模型

  • 單線程模型 這種模型下,所有互斥鎖都被禁用,同一時間只能由一個線程訪問。
  • 多線程模型 這種模型下,一個連接在同一時間內只有一個線程使用就是安全的。
  • 串行模型開啟所有鎖,可以隨意訪問。

設置線程模型

SQLite 可以通過以下三種方式進行線程模型的設置,在實際應用中選擇任一一項都可以。

  • 編譯期設定 通過 SQLITE_THREADSAFE 這個參數進行編譯器的設定來選擇線程模型
  • 初始化設定 通過調用 sqlite3_config() 可以在 SQLite 初始化時進行設定
  • 運行時設定 通過調用 sqlite3_open_v2() 接口指定數據庫連接的數據庫模型

SQLite 併發和事務

事務

事務是 SQLite 的核心概念。對數據庫的操作 (絕大部分) 會被打包成一個事務進行提交,需要注意的是,這裡的打包成事務是自動開啟的。舉例而言,如果簡單在一個 for 循環語句裡向數據庫中插入 10 條數據,意味著將自動生成 10 個事務。但需要注意的是事務是非常耗時的,一般而言, SQLite 每秒能夠輕鬆支持 50000 條的數據插入,但是每秒僅能夠支持幾十個事務。一般而言,事務速度受限於磁盤速度。所以在批量插入時需要考慮禁用自動提交,將其用 BEGIN ... COMMIT 打包成一個事務。

回滾模式和 WAL

為了保證寫入正確,SQLite 在使用事務進行數據庫改寫時將拷貝當前數據庫文件的備份,即 rollback journal,當事務失敗或者發生意外需要回滾時則將備份文件內容還原到數據庫中,並同時刪除該日誌。這是默認的 DELETE 模式。

而後 SQLite 也引入了 WAL 模式,即 Write-Ahead Log。在這種模式下,所有的修改會寫入一個單獨的 WAL 文件內。這種模式下,寫操作甚至可以不去操作數據庫,這使得所有的讀操作可以在 "寫的同時" 直接對數據庫文件進行操作,得到更好的併發性能。

鎖和併發

SQLite 通過五種鎖狀態來完成事務。

  • UNLOCKED ,無鎖狀態。數據庫文件沒有被加鎖。
  • SHARED 共享狀態。數據庫文件被加了共享鎖。可以多線程執行讀操作,但不能進行寫操作。
  • RESERVED 保留狀態。數據庫文件被加保留鎖。表示數據庫將要進行寫操作。
  • PENDING 未決狀態。表示即將寫入數據庫,正在等待其他讀線程釋放 SHARED 鎖。一旦某個線程持有 PENDING 鎖,其他線程就不能獲取 SHARED 鎖。這樣一來,只要等所有讀線程完成,釋放 SHARED 鎖後,它就可以進入 EXCLUSIVE 狀態了。
  • EXCLUSIVE 獨佔鎖。表示它可以寫入數據庫了。進入這個狀態後,其他任何線程都不能訪問數據庫文件。因此為了併發性,它的持有時間越短越好。

一個線程只有擁有低級別鎖時才能夠獲得更高一級的鎖

/*
** Lock the file with the lock specified by parameter eFileLock - one
** of the following:

**
** (1) SHARED_LOCK
** (2) RESERVED_LOCK
** (3) PENDING_LOCK
** (4) EXCLUSIVE_LOCK
**
** Sometimes when requesting one lock state, additional lock states
** are inserted in between. The locking might fail on one of the later
** transitions leaving the lock state different from what it started but
** still short of its goal. The following chart shows the allowed
** transitions and the inserted intermediate states:
**
** UNLOCKED -> SHARED
** SHARED -> RESERVED
** SHARED -> (PENDING) -> EXCLUSIVE
** RESERVED -> (PENDING) -> EXCLUSIVE
** PENDING -> EXCLUSIVE
**
** This routine will only increase a lock. Use the sqlite3OsUnlock()
** routine to lower a locking level.
*/

總結

綜上所述,要保證數據庫使用的安全,一般可以採用如下幾種模式

  • SQLite 採用單線程模型,用專門的線程/隊列(同時只能有一個任務執行訪問) 進行訪問
  • SQLite 採用多線程模型,每個線程都使用各自的數據庫連接 (即 sqlite3 *)
  • SQLite 採用串行模型,所有線程都公用同一個數據庫連接。

因為寫操作的併發性並不好,當多線程進行訪問時實際上仍舊需要互相等待,而讀操作所需要的 SHARED 鎖是可以共享的,所以為了保證最高的併發性,推薦

  • 使用多線程模式
  • 使用 WAL 模式
  • 單線程寫,多線程讀 (各線程都持有自己對應的數據庫連接)
  • 避免長時間事務
  • 緩存 sqlite3_prepare 編譯結果
  • 多語句通過 BEGIN 和 COMMIT 做顯示事務,減少多次的自動事務消耗

參考

SQLite 是否是線程安全的

Using SQLite In Multi-Threaded Applications

SQLite在多線程環境下的應用

SQLite 插入太慢

SQLite 自動提交

YapDatabase wiki 詳解


分享到:


相關文章: