簡體   English   中英

C# - 如何檢測 SQLite 數據庫是否被鎖定?

[英]C# - How to detect if SQLite DB is locked?

我正在開發一個使用 SQLite 的多線程 C# 程序。 我遇到了一個問題,有時運行 SQLiteCommand.ExecuteNonQuery() 來更新某些行會抱怨“SQLite 錯誤 (5):數據庫被鎖定”。 我知道發生這種情況是因為在進行插入或更新時數據庫被鎖定,因此如果出現另一個修改數據庫的查詢,第二個查詢將出現此數據庫被鎖定錯誤。 所以我正在嘗試對其實施解決方法,但我不確定我應該如何做。

我試圖這樣做,以便如果拋出數據庫鎖定錯誤,則程序會等待一段時間並再次嘗試,直到它工作為止。 但不知何故,沒有異常被捕獲,代碼只是退出 try-catch 塊,即使數據庫鎖定消息仍然打印在調試輸出中。 我不完全確定 SQL 查詢是被拒絕還是被接受。

我也嘗試過使用 TransactionScope 並且從那以后我還沒有數據庫被鎖定,但是由於問題的隨機性,我不能 100% 確定 TransactionScope 是否真的解決了問題,或者它是否只對一個程度,或者如果沒有,到目前為止我只是幸運。

SQLiteConnection connection = new SQLiteConnection("Data Source=DB.db;Version=3;");
connection.Open();
SQLiteCommand command = connection.CreateCommand();
command.commandText = inputQuery;
try
{
  command.executeNonQuery();
}
catch (SQLiteException sqle)
{
  Debug.WriteLine("Database error: " + e.Message);
  return false;
}
catch (Exception e)
{
  Debug.WriteLine("Database error: " + e.Message);
  return false;
}
finally
{
  connection.Close();
}

所以我真的很想有人幫我找出1)如何消除數據庫被鎖定的問題或2)如何檢測數據庫是否被鎖定錯誤發生。 提前致謝。

如果您收到錯誤代碼 5(忙碌),您可以通過使用立即事務來限制它。 如果您能夠立即開始事務,SQLite 保證您在提交之前不會收到忙錯誤。

另請注意,SQLite 沒有行級鎖定。 整個數據庫被鎖定。 使用 WAL 日志,您可以有一個作者和多個讀者。 使用其他日記方法,您可以擁有一個作者或多個讀者,但不能同時擁有兩者。

關於“SQLITE_BUSY”的 SQLite 文檔

為了檢測Sqlite DB是否被鎖定,我使用了確定SQLite數據庫是否被鎖定的方法這里的想法是嘗試獲取鎖並立即釋放它,如果它沒有失敗,那么DB沒有被鎖定。 以下 C# 代碼對我有用:

public bool IsDatabaseLocked(string dbPath)
    {
        bool locked = true;
        SQLiteConnection connection = new SQLiteConnection($"Data Source={dbPath};Version=3;");
        connection.Open();
        
        try
        {
            SQLiteCommand beginCommand = connection.CreateCommand();
            beginCommand.CommandText = "BEGIN EXCLUSIVE"; // tries to acquire the lock
            // CommandTimeout is set to 0 to get error immediately if DB is locked 
            // otherwise it will wait for 30 sec by default
            beginCommand.CommandTimeout = 0; 
            beginCommand.ExecuteNonQuery();

            SQLiteCommand commitCommand = connection.CreateCommand();
            commitCommand.CommandText = "COMMIT"; // releases the lock immediately
            commitCommand.ExecuteNonQuery();
            locked = false;
        }
        catch(SQLiteException)
        {
            // database is locked error
        }
        finally
        {
            connection.Close();
        }

        return locked;
    }

然后,當您確定 db 是否被鎖定時,您可以等待它被解鎖:

public async Task WaitForDbToBeUnlocked(string dbPath, CancellationToken token)
    {
        while (IsDatabaseLocked(dbPath))
        {
            await Task.Delay(TimeSpan.FromSeconds(1), token);
        }
    }

或在運行查詢之前將取消消息發送到其他線程(例如通過 CancellationTokenSource)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM