Sqlite3併發讀寫注意事項

最近項目中涉及到sqlite併發讀寫的問題,參考一些文檔並結合本身的實踐,對sqlite3併發問題總結了幾點:html

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來提交全部的修改或結束事務。

對SQLITE_BUSY的處理

當有多個鏈接同時對數據庫進行寫操做時,根據事務類型的使用原則,咱們在每一個鏈接中用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。而且,這兩個函數一經調用,對其後的全部數據庫操做都有效,很是方便。
參考:
相關文章
相關標籤/搜索