我有一个使用sqlite数据库的Web应用程序。 我的sqlite版本是官方Windows二进制发行版的最新版本 - 3.7.13。
问题是在数据库负载很重的情况下,sqlite API函数(例如sqlite3_step)正在返回SQLITE_BUSY。
初始化连接时,我传递了以下编译指示:
journal_mode = WAL
page_size = 4096
synchronous = FULL
foreign_keys = on
数据库是单文件数据库。我正在使用随它提供的Mono 2.10.8和Mono.Data.Sqlite程序集来访问数据库。
我正在用50个并行线程测试它,每个线程向我的应用程序发送50个后续的http请求。在每次请求时,都会对数据库进行一些读写操作。每组IO操作都在事务中执行。
一切顺利,直到接近第400至第700个请求。在这个(随机)时刻,API函数开始永久地返回SQLITE_BUSY(更准确地说 - 直到达到重试的限制)。
据我所知,WAL模式透明地支持并行读写。我猜测可能是因为在执行检查点操作时尝试读取数据库。但即使在关闭自动切换点后情况仍然如此。
在这种情况下可能出现什么问题? 如何正确提供大量并行数据库IO?
P.S。
每个请求只能连接一个连接。 我使用配置了WebSessionContext的nhibernate。
我像这样初始化我的NHibernate会话:
ISession session = null;
//factory variable is session factory
if (CurrentSessionContext.HasBind(factory))
{
session = factory.GetCurrentSession();
if (session == null)
CurrentSessionContext.Unbind(factory);
}
if (session == null)
{
session = factory.OpenSession();
CurrentSessionContext.Bind(session);
}
return session;
在HttpApplication.EndRequest上我发布它:
//factory variable is session factory
if (CurrentSessionContext.HasBind(factory))
{
try
{
CurrentSessionContext.Unbind(factory)
.Dispose();
}
catch (Exception ee)
{
Logr.Error("Error uninitializing session", ee);
}
}
据我所知,每个请求生命周期应该只有一个连接。在执行请求时,代码按顺序执行(ASP.NET MVC 3)。因此,这看起来并不像任何可靠性。我可以断定在这种情况下没有共享连接吗?
答案 0 :(得分:3)
我不清楚请求线程是否共享相同的连接。如果他们不这样做,那么你不应该遇到这些问题。
假设您确实在多个线程之间共享连接对象,您应该使用一些锁定机制作为SqliteConnection isn't thread-safe(一个旧帖子,但作为Mono的一部分维护的SQLite库是从System.Data演变而来的)。 SQLite在http://sqlite.phxsoftware.com)上找到。
因此,假设您没有使用SqliteConnection对象锁定,可以试试吗?完成此操作的简单方法可能如下所示:
static readonly object _locker = new object();
public void ProcessRequest()
{
lock (_locker) {
using (IDbCommand dbcmd = conn.CreateCommand()) {
string sql = "INSERT INTO foo VALUES ('bar')";
dbcmd.CommandText = sql;
dbcmd.ExecuteNonQuery();
}
}
}
但是,您可以选择与每个线程打开一个不同的连接,以确保您没有任何SQLite库的线程问题。
修改强>
对您发布的代码进行跟进,是否在提交事务后关闭会话?如果您不使用某些ITransaction,是否刷新并关闭会话?我问,因为我在你的代码中没有看到它,我在https://stackoverflow.com/a/43567/610650
中看到了它我也在http://nhibernate.info/doc/nh/en/index.html#session-configuration上看到它:
另请注意,您可以调用NHibernateHelper.GetCurrentSession();如 你喜欢多次,你将永远得到当前的ISession 这个HTTP请求。您必须确保ISession在之后关闭 您的工作单元在Application_EndRequest事件中完成 应用程序类中的处理程序或HTTP之前的HttpModule中的处理程序 回复已发送。