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

データベースは1ファイルのデータベースです。そして、データベースにアクセスするために、Mono2.10.8とそれに付属するMono.Data.Sqliteアセンブリを使用しています。

私は50の並列スレッドでテストしており、それぞれ50の後続のhttp要求をアプリケーションに送信しています。リクエストごとに、データベースに対して読み取りと書き込みが行われます。IO操作のすべてのセットは、トランザクション内で実行されます。

400番目から700番目のリクエストまですべてうまくいきます。この(ランダムな)瞬間に、API関数はSQLITE_BUSYを永続的に返し始めます(より正確には、再試行の制限に達するまで)。

私の知る限り、WALモードは並列読み取りと書き込みを透過的にサポートします。チェックポイント操作の実行中にデータベースを読み取ろうとしたことが原因である可能性があると推測しました。ただし、自動チェックポイントをオフにした後でも、状況は変わりません。

この状況で何が間違っている可能性がありますか?大量の並列データベースIOを正しく提供するにはどうすればよいですか?

PS

リクエストごとに1つの接続のみが想定されています。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);
  }
}

私の知る限り、リクエストのライフサイクルごとに接続は1つだけである必要があります。要求を処理している間、コードは順番に実行されます(ASP.NET MVC3)。したがって、ここでは一致が可能ではないように見えます。この場合、接続は共有されていないと結論付けることはできますか?

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を常に取得できます。作業単位の完了後、アプリケーションクラスのApplication_EndRequestイベントハンドラーまたはHTTP応答が送信される前のHttpModuleのいずれかで、ISessionが閉じられていることを確認する必要があります。

于 2012-08-29T12:00:29.900 に答える