SQLite庫級鎖簡介和“database is locked”異常的解決方法

SQLite 是一個軟件庫,實現了自給自足的、無服務器的、零配置的、事務性的 SQL 數據庫引擎。SQLite允許多個進程/線程同時進行讀操作,但在同一時刻只允許一個線程進行寫操作。SQLite在進行寫操作時,數據庫文件會被鎖定,此時任何其他的讀/寫操作都會被阻塞,如果阻塞超過5秒鐘(默認是5秒,可通過重新編譯SQLite進行修改),就會拋出描述爲“database is locked”的異常。

出現上述現象的原因是SQLite只支持庫級鎖,不支持併發執行寫操作,即使是不同的表,同一時刻也只能進行一個寫操作。例如,事務T1在表A新插入一條數據,事務T2在表B中更新一條已存在的數據,這兩個操作是不能同時進行的,只能順序進行。

SQLite儘量延遲了申請X鎖,直到數據塊真正寫盤時才申請X鎖,再加上被阻塞的操作有等待時間,所以當SQLite作爲客戶端嵌入數據庫被使用時時,一般情況下不會拋出“database is locked”的異常。但是,在高併發的環境下,還是很有可能拋出異常的。避免這種異常的最簡單有效的方法,就是在進行寫操作時實現互斥鎖,並保證寫操作按順序執行。

下面是C#代碼中的簡單示例,UpdateData是用於執行數據庫操作的函數。

try
{
    lock (sqlservice)
    {
        bool result = sqlservice.UpdateData(out error, sqlCmd);
        if (!result || !string.IsNullOrEmpty(error))
        {
            return false;
        }

        return true;
    }
}
catch (Exception ex)
{
    return false;
}

爲了進一步優化高併發環境下SQLite的寫操作,避免造成死鎖,可以使用Monitor類實現對數據庫文件的資源調度,.lock關鍵字實際上就是對Monitor對象進行封裝,給object加上了一個互斥鎖。比如使用Monitor.Wait()和Monitor.Pulse()函數在多線程環境下對資源所有權進行合理分配,在線程A的lock代碼裏調用Monitor.Wait(),會放棄對資源的佔用,讓線程B可以佔用該資源。然後在佔用資源的線程B的代碼裏調用Monitor.Pulse(),讓線程A進入等待隊列。線程B完成操作後,再調用Monitor.Wait()釋放資源,這樣線程A就可以繼續執行了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章