簡體   English   中英

SQLite 數據庫鎖定異常

[英]SQLite Database Locked exception

我只對某些查詢從SQLite獲取數據庫被鎖定異常。

下面是我的代碼:當我執行任何選擇語句時,它工作正常。
當我在Jobs Table 上執行任何 write 語句時,它也可以正常工作。

這工作正常:

ExecuteNonQuery("DELETE FROM Jobs WHERE id=1");

但同樣,如果我正在執行對Employees表的查詢,它會引發數據庫被鎖定的異常。
這會引發異常:

ExecuteNonQuery("DELETE FROM Employees WHERE id=1");

以下是我的功能:

public bool OpenConnection()
{
    if (Con == null)
    {
        Con = new SQLiteConnection(ConnectionString);
    }
    if (Con.State == ConnectionState.Closed)
    {
        Con.Open();
        //Cmd = new SQLiteCommand("PRAGMA FOREIGN_KEYS=ON", Con);
        //Cmd.ExecuteNonQuery();
        //Cmd.Dispose();
        //Cmd=null;
        return true;
    }
    if (IsConnectionBusy())
    {
        Msg.Log(new Exception("Connection busy"));
    }
    return false;
}

public Boolean CloseConnection()
{
    if (Con != null && Con.State == ConnectionState.Open)
    {
        if (Cmd != null) Cmd.Dispose();
        Cmd = null;
        Con.Close();
        return true;
    }

    return false;
}

public Boolean ExecuteNonQuery(string sql)
{
    if (sql == null) return false;
    try
    {
        if (!OpenConnection())
            return false;
        else
        {
            //Tx = Con.BeginTransaction(IsolationLevel.ReadCommitted);
            Cmd = new SQLiteCommand(sql, Con);
            Cmd.ExecuteNonQuery();
            //Tx.Commit();
            return true;
        }
    }
    catch (Exception exception)
    {
        //Tx.Rollback();
        Msg.Log(exception);
        return false;
    }
    finally
    {
        CloseConnection();
    }
}

這是例外:在第 103 行: Cmd.ExecuteNonQuery();

發現異常:類型:System.Data.SQLite.SQLiteException 消息:數據庫已鎖定 數據庫已鎖定 源:System.Data.SQLite

Stacktrace:在 System.Data.SQLite.SQLiteDataReader.NextResult() 在 System.Data.SQLite.SQLiteDataReader..ctor(SQLiteCommand cmd,CommandBehavior 行為) 在 System.Data 的 System.Data.SQLite.SQLite3.Step(SQLiteStatement stmt) .SQLite.SQLiteCommand.ExecuteReader(CommandBehavior 行為) 在 System.Data.SQLite.SQLiteCommand.ExecuteNonQuery() 在 TimeSheet6.DbOp.ExecuteNonQuery(String sql) 在 d:\Projects\C# Applications\Completed Projects\TimeSheet6\TimeSheet6\DbOp。 CS:第 103 行

在某個地方,連接處於打開狀態。 擺脫OpenConnectionCloseConnection並將ExecuteNonQuery更改為:

using (SQLiteConnection c = new SQLiteConnection(ConnectionString))
{
    c.Open();
    using (SQLiteCommand cmd = new SQLiteCommand(sql, c))
    {
        cmd.ExecuteNonQuery();
    }
}

此外,將讀取數據的方式更改為:

using (SQLiteConnection c = new SQLiteConnection(ConnectionString))
{
    c.Open();
    using (SQLiteCommand cmd = new SQLiteCommand(sql, c))
    {
        using (SQLiteDataReader rdr = cmd.ExecuteReader())
        {
            ...
        }
    }
}

不要嘗試像在這里一樣自行管理連接池。 首先,它比您編寫的代碼要復雜得多,但其次,它已經在SQLiteConnection對象中進行了處理。 最后,如果您沒有利用using ,那么您就沒有正確處理這些對象,最終會遇到您現在看到的問題。

您可以使用下面的“使用”語句,即使在異常情況下也可以確保正確處理連接和命令

private static void ExecuteNonQuery(string queryString)
{
    using (var connection = new SQLiteConnection(
               ConnectionString))
    {
        using (var command = new SQLiteCommand(queryString, connection))
        {
            command.Connection.Open();
            command.ExecuteNonQuery();
        }
    }
}

在嘗試將任何數據寫入數據庫之前,您應該關閉 DataReader。 利用:

dr.Close();

使用完 DataReader 后。

在我的情況下,我非常愚蠢,我在 SQLite 瀏覽器中進行更改並且沒有單擊寫入更改,這鎖定了要由服務修改的數據庫。 單擊“寫入更改”按鈕后,所有發布請求都按預期工作。

對於那些可能忘記清理懸空連接的人來說,這里有很多有用的帖子,但還有另一種可能發生的方式:SQLite 不支持並發 INSERT; 如果您同時發出兩個 INSERT,則將按順序處理。 當 INSERT 很快時,這很好,但如果 INSERT 花費的時間超過超時時間,則第二個 INSERT 可能會失敗並顯示此消息。

當我使用長時間運行的事務將一堆 INSERT 累積到一個大提交中時,我發生了這種情況。 基本上,我在事務期間將數據庫鎖定在任何其他活動中。 切換到journal_mode=WAL將允許並發寫入和讀取,但不允許並發寫入。

我擺脫了長時間運行的事務,讓每個 INSERT 自動提交,這解決了我的問題。

我在這里也遇到了同樣的錯誤:

if (new basics.HindiMessageBox(HMsg, HTitle).ShowDialog()==true)
{
    SQLiteConnection m_dbConnection = new SQLiteConnection(MainWindow.con);
    m_dbConnection.Open();
    sql = "DELETE FROM `users`  WHERE `id`=" + SelectedUser.Id;
    command = new SQLiteCommand(sql, m_dbConnection);
    command.ExecuteNonQuery();
    m_dbConnection.Close();
    LoadUserDG();
}

但是當我剛剛更改 SQLiteConnection 聲明位置時

public partial class User : Window
{
    SQLiteCommand command;
    string sql;
    AddUser AddUserObj;
    List<basics.users> usersList;
    basics.users SelectedUser;
    SQLiteConnection m_dbConnection;

    // ...

    private void DeleteBtn_Click(object sender, RoutedEventArgs e)
    {
        // ...
        if (new basics.HindiMessageBox(HMsg, HTitle).ShowDialog()==true)
        {
            m_dbConnection = new SQLiteConnection(MainWindow.con);
            m_dbConnection.Open();
            sql = "DELETE FROM `users`  WHERE `id`=" + SelectedUser.Id;
            command = new SQLiteCommand(sql, m_dbConnection);
            command.ExecuteNonQuery();
            m_dbConnection.Close();
            LoadUserDG();
        }
}

現在一切都很好。 我希望這也對你有用。 如果有人能說出這是怎么發生的,我想知道細節以提高我的知識,拜托。

從多個線程將大量數據加載到不同的表時,我遇到了同樣的問題。 在嘗試進行插入時,我將數據庫鎖定,因為程序插入太多太快,SQLite 沒有時間在另一個事務到來之前完成每個事務。

插入是通過線程完成的,因為我不希望接口被鎖定並等待插入完成。

我的解決方案是將 BlockingCollection 與 ThreadPool.QueueUserWorkItem 一起使用。 這使我可以在插入時釋放界面。 所有插入都按 FIFO(先進先出)順序排隊和執行。 現在,在從任何線程執行任何 SQL 事務時,數據庫永遠不會被鎖定。

public class DatabaseQueueBus
{
    private BlockingCollection<TransportBean> _dbQueueBus = new BlockingCollection<TransportBean>(new ConcurrentQueue<TransportBean>());
    private CancellationTokenSource __dbQueueBusCancelToken;

    public CancellationTokenSource _dbQueueBusCancelToken { get => __dbQueueBusCancelToken; set => __dbQueueBusCancelToken = value; }

    public DatabaseQueueBus()
    {
        _dbQueueBusCancelToken = new CancellationTokenSource();
        DatabaseQueue();

    }

    public void AddJob(TransportBean dto)
    {
        _dbQueueBus.Add(dto);
    }

    private void DatabaseQueue()
    {

        ThreadPool.QueueUserWorkItem((param) =>
        {
            try
            {
                do
                {
                    string job = "";
                    TransportBean dto = _dbQueueBus.Take(_dbQueueBusCancelToken.Token);
                    try
                    {
                        job = (string)dto.DictionaryTransBean["job"];
                        switch (job)
                        {
                            case "SaveClasse":
                                //Save to table here
                                break;
                            case "SaveRegistrant":
                                //Save Registrant here
                                break;
                          
                        }
                    }
                    catch (Exception ex)
                    {//TODO: Handle this exception or not

                    }

                } while (_dbQueueBusCancelToken.Token.IsCancellationRequested != true);
            }
            catch (OperationCanceledException)
            {

            }
            catch (Exception ex)
            {

            }
        });
    }
}

插入是以這種方式完成的,但沒有排隊我仍然遇到鎖定問題。

using (SQLiteConnection c = new SQLiteConnection(BaseDal.SQLiteCon))
        {
            c.Open();
            using (SQLiteCommand cmd = new SQLiteCommand(sql, c))
            {
                cmd.ExecuteNonQuery();
            }
            c.Close();
        }

我的原因是調用 HasRows() 時沒有關閉 SqliteDataReader。

我有這個:

 using (SQLiteConnection connection = new SQLiteConnection(DbPath))
 {
    connection.Open();
    string sql = $"SELECT * FROM ...";
    using (SQLiteCommand command = new SQLiteCommand(sql, connection))
    {
        return command.ExecuteReader().HasRows;
    }

    connection.Close();
}

但是需要像這樣在 ExecuteReader 周圍使用:

using (SQLiteDataReader reader = command.ExecuteReader())
{
      return command.ExecuteReader().HasRows;
}

即使每次數據庫仍然被讀者鎖定時,都在處理和重新創建 DbConnection。

就我而言,關閉連接不是問題。 問題是我打開和關閉每個表的連接,盡管數據庫是相同的。 所以不要在 SQlite 中為同一個數據庫多次打開連接。

  • 打開連接
  • 僅在該打開的連接中進行所有交易。
  • 關閉連接。

SQlite 不擅長處理這個問題。 因此,如果您負擔不起該解決方案,最好使用 SQLServer 等服務器。

暫無
暫無

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

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