[英]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.