4

我有一个使用 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?

附言

假设每个请求只有一个连接。我使用配置了 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)。所以看起来这里不可能有任何并发​​性。我可以得出结论,在这种情况下没有共享连接吗?

4

1 回答 1

3

我不清楚请求线程是否共享相同的连接。如果他们不这样做,那么您不应该遇到这些问题。

假设您确实在多个线程之间共享连接对象,您应该使用一些锁定机制,因为SqliteConnection 不是线程安全的(一个旧帖子,但是作为 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(); 只要你喜欢,你总是会得到这个 HTTP 请求的当前 ISession。您必须确保 ISession 在您的工作单元完成后关闭,无论是在应用程序类的 Application_EndRequest 事件处理程序中,还是在发送 HTTP 响应之前的 HttpModule 中。

于 2012-08-29T12:00:29.900 回答