sqlite3并發讀寫和事務死鎖問題
標簽: 數據庫
最近項目中涉及到sqlite并發讀寫的問題,最終發現基線兩個數據庫使用同一個db_connect()接口,都存在并發訪問沖突隱患,但只在H11平臺上出現。是因為其它平臺性能好,“只要你CPU執行速度夠快,我dhcp就能完美錯開sipServer初始化,避免沖突” 。
參考一些文檔并結合自己的實踐,對sqlite3并發問題總結了幾點:
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。并且,這兩個函數一經調用,對其后的所有數據庫操作都有效,非常方便。
解決方法:
綜上,我們不難發現并發讀寫的時候出現了事務死鎖,最終解決方法如下:
法一:信號量實現互斥
sem_p(semid, 0);
sqlite3_exec(db, buf, 0, 0, &pErrMsg);
sem_v(semid, 0);
法二:自定義循環訪問
do
{
ret = sqlite3_exec(db, buf, 0, 0, &pErrMsg);
if (ret == SQLITE_BUSY)
{
sleep(1);
continue;
}
break;
}while(1);
法三:使用sqlite3的API,當檢測到當前連接的數據庫處于SQLITE_BUSY時等待,或自定義busy時的回調處理
參考:
1、https://www.cnblogs.com/hustcat/archive/2009/03/01/1400757.html
2、https://www.cnblogs.com/lijingcheng/p/4454884.html
3、https://www.jianshu.com/p/15051fbd5a35?utm_campaign
智能推薦
Sqlite3性能測試
參考:http://blog.csdn.net/majiakun1/article/details/46607163,感謝作者分享。 Sqlite3最簡單的提升讀寫性能的方法有: 1. 關閉寫同步,也就是設置synchronous。Sqlite3是一個文件數據庫,所謂的設置寫同步就是設置每次寫完數據之后刷新IO緩存的頻率,如果寫同步設置為Full,那么每次寫都會刷新緩存,這樣保證數據總能寫到文件里...
【sqlite3】基本使用
Overview 1. 準備 1.1 Windows 1.1.1 下載 1.1.2 安裝 1.2 LINUX(Ubuntu) 2. 使用 2.1 Windows 2.2 LINUX(Ubuntu) 2.3 執行 sql 腳本文件 3. 基本配置:數據編碼等 3.1 編碼:utf-8 4. 數據庫命令對比 5. SQL 對比 5.1 數據類型 Reference 1. 準備 1.1 Windows ...
Python操作SQLite3
1、連接數據庫 2、創建數據表 3、在2的基礎上插入數據 4、在3的基礎上查找數據 更多操作:https://blog.csdn.net/hhy1107786871/article/details/86540125?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522160024824419725264625847%2522%252C%2...
裸機移植sqlite3
上一篇文章中提供了vfs支持,此處主要是sqlite3移植所需的線程,信號量支持,sqlite3源碼在官方下載,需要很大的內存支持,移植基本原理就是實現sqlite3所需的linux api接口即可,可以通過宏定義去掉一些不用的功能,減少API依賴。 ucos_iii_pthread.c //ucos_iii_pthread.h 請先初始化ucos以及文件系統,然后就可以初始化sqlite3,并查...
Sqlite3交叉編譯
一、準備工作 本次使用ubuntu18.04和 "arm-fsl-linux-gnueabi-"交叉編譯器 1、下載Sqlite3 在https://www.sqlite.org/download.html里下載Sqlite3 2、將交叉編譯工具鏈安裝 1、查看交叉編譯器是否在環境變量中 加入環境變量的方法: 1、sudo vi /etc/bash.bashrc 2、輸入exp...
猜你喜歡
freemarker + ItextRender 根據模板生成PDF文件
1. 制作模板 2. 獲取模板,并將所獲取的數據加載生成html文件 2. 生成PDF文件 其中由兩個地方需要注意,都是關于獲取文件路徑的問題,由于項目部署的時候是打包成jar包形式,所以在開發過程中時直接安照傳統的獲取方法沒有一點文件,但是當打包后部署,總是出錯。于是參考網上文章,先將文件讀出來到項目的臨時目錄下,然后再按正常方式加載該臨時文件; 還有一個問題至今沒有解決,就是關于生成PDF文件...
電腦空間不夠了?教你一個小秒招快速清理 Docker 占用的磁盤空間!
Docker 很占用空間,每當我們運行容器、拉取鏡像、部署應用、構建自己的鏡像時,我們的磁盤空間會被大量占用。 如果你也被這個問題所困擾,咱們就一起看一下 Docker 是如何使用磁盤空間的,以及如何回收。 docker 占用的空間可以通過下面的命令查看: TYPE 列出了docker 使用磁盤的 4 種類型: Images:所有鏡像占用的空間,包括拉取下來的鏡像,和本地構建的。 Con...
requests實現全自動PPT模板
http://www.1ppt.com/moban/ 可以免費的下載PPT模板,當然如果要人工一個個下,還是挺麻煩的,我們可以利用requests輕松下載 訪問這個主頁,我們可以看到下面的樣式 點每一個PPT模板的圖片,我們可以進入到詳細的信息頁面,翻到下面,我們可以看到對應的下載地址 點擊這個下載的按鈕,我們便可以下載對應的PPT壓縮包 那我們就開始做吧 首先,查看網頁的源代碼,我們可以看到每一...