[英]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 行
在某個地方,連接處於打開狀態。 擺脫OpenConnection
和CloseConnection
並將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.