Sqlite3併發讀寫注意事項

2021-08-28 02:29:26 字數 2065 閱讀 5389

最近專案中涉及到sqlite併發讀寫的問題,參考一些文件並結合自己的實踐,對sqlite3併發問題總結了幾點:

sqlite3總共有三種事務型別:begin [deferred /immediate / exclusive] transcation,五種鎖,按鎖的級別依次是:unlocked /shared /reserverd /pending /exclusive。當執行select即讀操作時,需要獲取到shared鎖(共享鎖),當執行insert/update/delete操作(即記憶體寫操作時),需要進一步獲取到reserverd鎖(保留鎖),當進行commit操作(即磁碟寫操作時),需要進一步獲取到exclusive鎖(排它鎖)。

對於reserverd鎖,sqlite3保證同一時間只有乙個連線可以獲取到保留鎖,也就是同一時間只有乙個連線可以寫資料庫(記憶體),但是其它連線仍然可以獲取shared鎖,也就是其它連線仍然可以進行讀操作(這裡可以認為寫操作只是對磁碟資料的乙份記憶體拷貝進行修改,並不影響讀操作)。

對於exclusive鎖,是比保留鎖更為嚴格的一種鎖,在需要把修改寫入磁碟即commit時需要在保留鎖/未決鎖的基礎上進一步獲取到排他鎖,顧名思義,排他鎖排斥任何其它型別的鎖,即使是shared鎖也不行,所以,在乙個連線進行commit時,其它連線是不能做任何操作的(包括讀)。

pending鎖(即未決鎖),則是比較特殊的一種鎖,它可以允許已獲取到shared鎖的事務繼續進行,但不允許其它連線再獲取shared鎖,當已存在的shared鎖都被釋放後(事務執行完成),持有未決鎖的事務就可以獲得commit的機會了。sqlite3使用這種鎖來防止writer starvation(寫餓死)。

死鎖的情況:當兩個連線使用begin transaction開始事務時,第乙個連線執行了一次select操作(已經獲取到shared鎖),第二個連線執行了一次insert操作(已經獲取到了reserverd鎖),此時第乙個連線需要進行一次insert/update/delete(需要獲取到reserverd鎖),第二個連線則希望執行commit(需要獲取到exclusive鎖),由於第二個連線已經獲取到了reserverd鎖,根據reserverd鎖同一時間只有乙個連線可以獲取的特性,第乙個連線獲取reserverd鎖的操作必定失敗,而由於第乙個連線已經獲取到shared鎖,第二個連線希望進一步獲取到exclusive鎖的操作也必定失敗。就導致了事務死鎖。

在用」begin transaction」顯式開啟乙個事務時,預設的事務型別為deferred,鎖的狀態為unlocked,即不獲取任何鎖,如果在使用的資料庫沒有其它的連線,用begin就可以了。如果有多個連線都需要對資料庫進行寫操作,那就得使用begin immediate/exclusive開始事務了。

使用事務的好處是:1.乙個事務的所有操作相當於一次原子操作,如果其中某一步失敗,可以通過回滾來撤銷之前所有的操作,只有當所有操作都成功時,才進行commit,保證了操作的原子特性;2.對於多次的資料庫操作,如果我們希望提高資料查詢或更新的速度,可以在開始操作前顯式開啟乙個事務,在執行完所有操作後,再通過一次commit來提交所有的修改或結束事務。

當有多個連線同時對資料庫進行寫操作時,根據事務型別的使用原則,我們在每個連線中用begin immediate開始事務,即多個連線都嘗試取得保留鎖的情況,根據保留鎖同一時間只有乙個連線可以獲取到的特性,其它連線都將獲取失敗,即事務開始失敗,這種情況下,sqlite3將返回乙個sqlite_busy的錯誤,如果我們不希望操作就此失敗而返回,就必須處理sqlite_busy的情況,sqlite3提供了sqlite3_busy_handler或sqlite3_busy_timeout來處理sqlite_busy,對於sqlite3_busy_handler,我們可以指定乙個busy_handler來處理,並可以指定失敗重試的次數。而sqlite3_busy_timeout則是由sqlite3自動進行sleep並重試,當sleep的累積時間超過指定的超時時間時,最終返回sqlite_busy。需要注意的是,這兩個函式同時只能使用乙個,後面的呼叫會覆蓋掉前次呼叫。從使用上來說,sqlite3_busy_timeout更易用一些,只需要指定乙個總的超時時間,然後sqlite自己會決定多久進行重試以及重試的次數,直到達到總的超時時間最終返回sqlite_busy。並且,這兩個函式一經呼叫,對其後的所有資料庫操作都有效,非常方便。

參考:

SQLite 3的中文讀寫

呼叫sqlite3 open函式預設建立的資料庫encoding utf 8,執行sqlite3 exec時需要將對應的字串轉換為utf 8格式多位元組字串。比如 sqlite3 db auto retval sqlite3 open test.db db char perrmsg auto sql...

SQLite 3的中文讀寫

呼叫sqlite3 open函式預設建立的資料庫encoding utf 8,執行sqlite3 exec時需要將對應的字串轉換為utf 8格式多位元組字串。比如 sqlite3 db auto retval sqlite3 open test.db db char perrmsg auto sql...

併發程式設計注意事項

一般在執行批處理的場景下,如果序列單執行緒的效率低,可以考慮併發,但是需要注意一些事情。首先需要確定,單個執行緒執行的場景下,耗時的 塊,一般的做法是通過列印執行時間,然後優化到合適的地步。採用多執行緒來執行任務,由於併發的不確定性以及併發引入的複雜性,要能夠保證併發處理的結果是正確的,是準確的。既...