1. 程式人生 > >sqlite的執行緒安全與併發

sqlite的執行緒安全與併發

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 做顯示事務,減少多次的自動事務消耗

參考