2009-08-03 29 views
10

SQLiteデータベースに対して多数のINSERTを実行しています。私はただ1つのスレッドを使用しています。私はパフォーマンスを改善するために書き込みをバッチし、クラッシュの場合には少しのセキュリティを持っています。基本的には、私はたくさんのデータをメモリにキャッシュしておき、それが適切だと思うと、そのデータすべてをループしてINSERTを実行します。このためのコードは次のようになります。 SQLiteコミット中にデータベースファイルが不可能にロックされています

private void TryHandleCommit(SQLiteTransaction trans) 
    { 
     try 
     { 
      trans.Commit(); 
     } 
     catch (Exception e) 
     { 
      Console.WriteLine("Trying again..."); 
      this.TryHandleCommit(trans); 
     } 
    } 

私はそうのように私のDBを作成します:

public void Commit() 
    { 
     using (SQLiteConnection conn = new SQLiteConnection(this.connString)) 
     { 
      conn.Open(); 
      using (SQLiteTransaction trans = conn.BeginTransaction()) 
      { 
       using (SQLiteCommand command = conn.CreateCommand()) 
       { 
        command.CommandText = "INSERT OR IGNORE INTO [MY_TABLE] (col1, col2) VALUES (?,?)"; 

        command.Parameters.Add(this.col1Param); 
        command.Parameters.Add(this.col2Param); 

        foreach (Data o in this.dataTemp) 
        { 
         this.col1Param.Value = o.Col1Prop; 
         this. col2Param.Value = o.Col2Prop; 

         command.ExecuteNonQuery(); 
        } 
       } 
       this.TryHandleCommit(trans); 
      } 
      conn.Close(); 
     } 
    } 

が、私は今、最終的に仕事をする事を取得するには、次のギミックを採用

public DataBase(String path) 
    { 
     //build connection string 
     SQLiteConnectionStringBuilder connString = new SQLiteConnectionStringBuilder(); 
     connString.DataSource = path; 
     connString.Version = 3; 
     connString.DefaultTimeout = 5; 
     connString.JournalMode = SQLiteJournalModeEnum.Persist; 
     connString.UseUTF16Encoding = true; 

     using (connection = new SQLiteConnection(connString.ToString())) 
     { 
      //check for existence of db 
      FileInfo f = new FileInfo(path); 

      if (!f.Exists) //build new blank db 
      { 
       SQLiteConnection.CreateFile(path); 
       connection.Open(); 

       using (SQLiteTransaction trans = connection.BeginTransaction()) 
       { 
        using (SQLiteCommand command = connection.CreateCommand()) 
        { 
         command.CommandText = DataBase.CREATE_MATCHES; 
         command.ExecuteNonQuery(); 

         command.CommandText = DataBase.CREATE_STRING_DATA; 
         command.ExecuteNonQuery(); 
         //TODO add logging 
        } 
        trans.Commit(); 
       } 
       connection.Close(); 
      } 
     }    
    } 

次に、接続文字列をエクスポートして、プログラムのさまざまな部分で新しい接続を取得するために使用します。

一見無作為な間隔で、この問題を無視するかそれ以外の方法で解決するにはあまりにも速い速度ですが、未処理SQLiteExceptionが発生します。データベースファイルがロックされています。これは、トランザクションをコミットしようとしたときに発生します。それ以前にはエラーは発生していないようです。これはありません常にが発生します。時には全体が邪魔になることなく走ることもあります。

  • コミットが完了する前に、これらのファイルに対する読み取りは実行されていません。
  • 私は最新のSQLiteバイナリを持っています。
  • .NET 2.0用にコンパイルしています。
  • 私はVS 2008を使用しています。
  • dbはローカルファイルです。
  • このアクティビティはすべて1つのスレッド/プロセス内にカプセル化されています。
  • ウイルス保護はオフです(ただし、ネットワーク経由で接続していた場合のみ有効です)。
  • Cに
  • DBファイルを永続化するために設定され保存された
  • ジャーナルモード:ドキュメント\ + Settings \のapplicationDataにSystem.Windows.Forms.Application.AppDataウィンドウが呼び出しを経由して
  • ない内部例外
  • スコットランドのポストごとに、私は次の変更を実施してきたように
  • 2つの別個のマシン(非常に似たハードウェアとソフトウェアですが)でも確認されています
  • プロセスモニターを実行しています - 異質なプロセスがDBファイルにアタッチしていません - 問題は間違いなく私のコードです...

ここでは何が起こっているのでしょうか?

私はちょうどコードの混乱を落としてしまったことは知っていますが、私はあまりにも長くこのことを理解しようとしています。この質問の終わりまでそれを作る誰にも感謝します!

ブライアン

UPDATES:これまでに提案を

ありがとう!私は提案された変更の多くを実装しました。しかし私はそれが非決定論的である、我々は技術的に働く上...しかし...

コードを答えに近づいていることを感じます!中立のスピンから永遠に何かを行うことは保証されていません。実際には、1回目と10回目の繰り返しの間のどこかで動作するようです。合理的な期間のダメージで私バッチ私のコミットが軽減されますが、私は本当にこの状態で物事を残したくない場合は...

もっと提案を歓迎します!

+0

コミットを小さくしたくはありませんが、begin tranを移動してtranを内側のバッチループにコミットするときに問題が発生しますか? – pjp

答えて

5

Sysinternals Process Monitorを実行し、プログラムを実行している間にファイル名をフィルタリングして、他のプロセスが何かを実行していない場合は除外し、プログラムがファイルに対して行っていることを確認します。ロングショットだが、手がかりを与えるかもしれない。

+1

これは...ファイルをロックしている別のプロセスの問題でしたか? – Rory

2

はアプリと同じマシン上のデータベースファイルであるか、サーバーに保存されていますか?

すべてのスレッドで新しい接続を作成する必要があります。私は接続の作成を簡単に行い、どこでも使えます:connection = new SQLiteConnection(connString.ToString());

と再びアプリとテストと同じマシン上のデータベースファイルを使用します。

なぜ2つの異なる接続方法がありますか?監視する

+0

私はこれを実装しました。幸運なことはありませんが、良いスタートは確かです!どうも! –

3

もの:

  • は、複数のスレッド/プロセス間の接続を使用しないでください。

  • ウイルススキャナがファイルの変更を検出してスキャンしようとすると、私はそれが起こるのを見ました。短い間隔でファイルをロックし、混乱を引き起こします。

+1

ちょうどそれを試みた - 私はそれが問題ではなかったと思うが、とにかく感謝! –

2

これらの人は(ほとんどが、それは多分、TortoiseSVNの相互作用、ロックされているジャーナリングファイルで、表示されます...参照記事をご確認ください)似問題を抱えました。

彼らは勧告(正しいディレクトリ、持続するには、deleteからジャーナリングの種類を変更する、など)のセットを思い付きました。 http://sqlite.phxsoftware.com/forums/p/689/5445.aspx#5445


ジャーナルモードのオプションは、ここで説明されている:http://www.sqlite.org/pragma.html。 TRUNCATEを試すことができます。

SQL Liteの例外時にスタックトレースがありますか?

あなたが「合理的な間隔でバッチの私のコミット」を示します。間隔は何ですか?

+0

私は実際に亀を使用しています - 私は返信し、それが助けになるかどうかをお知らせします... –

+0

を使用すると、この問題の頻度は減少しているかもしれませんが、それは排除されません。 UserAppDataも良い呼び出しでした。ありがとう! –

+0

だからどこがあなたを去るのですか?私はそれが問題ではないと確信している間にあなたはコミットを近づける(時間的に)可能性がありますか? – BlueShepherd

2

私は常にusing句でConnection、Transaction、およびCommandを使用します。あなたの最初のコードリストでは行いましたが、あなたがしなかったのは3番目のテーブル作成です。私はあなたもそれをすることをお勧めします。誰が知っているのでしょうか?おそらく、テーブルを作成するコマンドが何らかの形でファイルをロックし続けます。ロングショット...でも価値はある?

+0

残念ながらそれはそのトリックをしませんでしたが、それはおそらくそれを行うためのよりよい方法ですので、私はそれに応じて私のコードを更新しました... thx! –

2

Googleデスクトップ検索(または別のファイルインデクサー)を実行していますか?前述したように、Sysinternals Process Monitorは、あなたがそれを追跡するのを助けることができます。

また、データベースのファイル名は何ですか?PerformanceTuningWindowsから:あなたはすべてのデータベースに拡張.SDB(SQLiteデータベース、素敵な名前を付けた場合

、あなたのデータベース、例えば特に拡張

に名前を付けるものを非常に、非常に注意してくださいちょっと私が思いました?とにかくそれを選択すると... SDB拡張機能が既にAPPFIXパッケージに関連付けられていることがわかります。

さて、ここでキュートな一部である、APPFIXは、それが(強調鉱山)システムにデータベースを追加する機能

これは意味を復元、するWindows XPが認識し、実行可能/パッケージである、そして、ここで私と一緒に滞在何かをデータベースに書き込むたびに、Windows XPシステムでは、実行可能ファイルが変更されたと判断し、ENTIRE 800 megデータベースをシステムの復元ディレクトリにコピーします。

DBやDATのようなものをお勧めします。

9

作成したトランザクションにコマンドをリンクできなかったようです。 の代わりに:

あなたが使用する必要があります
using (SQLiteCommand command = conn.CreateCommand()) 

using (SQLiteCommand command = new SQLiteCommand("<INSERT statement here>", conn, trans)) 

それとも、その構築後にそのトランザクションプロパティを設定することができます。私たちはそれでありながら

は - 失敗のあなたの取り扱いが正しくありません:

コマンドのExecuteNonQueryメソッドも失敗することができますし、実際に保護されていません。コードを次のように変更する必要があります。

public void Commit() 
    { 
     using (SQLiteConnection conn = new SQLiteConnection(this.connString)) 
     { 
      conn.Open(); 
      SQLiteTransaction trans = conn.BeginTransaction(); 
      try 
      { 
       using (SQLiteCommand command = conn.CreateCommand()) 
       { 
        command.Transaction = trans; // Now the command is linked to the transaction and don't try to create a new one (which is probably why your database gets locked) 
        command.CommandText = "INSERT OR IGNORE INTO [MY_TABLE] (col1, col2) VALUES (?,?)"; 

        command.Parameters.Add(this.col1Param); 
        command.Parameters.Add(this.col2Param); 

        foreach (Data o in this.dataTemp) 
        { 
         this.col1Param.Value = o.Col1Prop; 
         this. col2Param.Value = o.Col2Prop; 

         command.ExecuteNonQuery(); 
        } 
       } 

       trans.Commit(); 
      } 
      catch (SQLiteException ex) 
      { 
       // You need to rollback in case something wrong happened in command.ExecuteNonQuery() ... 
       trans.Rollback(); 
       throw; 
      } 
     } 
    } 

もう1つのことは、メモリに何もキャッシュする必要がないということです。不完全なトランザクション状態を格納するためのSQLiteジャーナリングメカニズムに依存することができます。

+0

グッド私はこれをすべて消化して試してみるのに時間が必要ですが、入力に感謝します! –

+0

また、キャッシュについて言及してうれしいです。もともと私はあなたが提案する方法でそれをやっていましたが、それがこのコミット失敗の混乱に寄与していると懸念して以来、それから離れました。私はそれを働かせたら、私は戻っていくつかの適切なコードサンプルを投稿します... –

+0

私は両方の方法を試してみました - トランザクションを渡してプロパティを設定することで構築しました。どちらも私の問題を緩和していません。提案のおかげでtho! –

4

私たちは、TransactionScopeクラスでネストされたトランザクションを使用することで非常に似た問題を抱えていました。私たちはと思ってすべてのデータベースアクションが同じスレッドで発生しました...しかし、私たちはトランザクションメカニズム、具体的にはAmbientトランザクションによってキャッチされました。

基本的には、魔法の魔法によって自動的に接続が確立されたチェーン上のトランザクションがありました。その結果、単一のスレッドでデータベースに書き込んでいるとは思っていましたが、書き込みdidn一番上のトランザクションがコミットされるまでは本当に起こりません。この「不確定」点では、データベースが書き込まれて制御不能にロックされていました。私はASPを勉強しています:私は今日、これと同じ問題に直面し始め

using(TransactionScope scope = new TransactionScope(TransactionScopeOptions.RequiresNew)) 
{ 
    ... 
    scope.Complete() 
} 
3

ソリューションは、SQLiteのデータベースに直接我々のようなものを使用し確保することによって、周囲のトランザクションに参加しなかったことを確実にするためでした。 net mvc、私の最初のアプリケーションをゼロから完全に構築する。時には、私がデータベースに書き込むときに、同じ例外が出て、データベースファイルがロックされていると言います。

私は、(プロセスエクスプローラのアクティブなファイルハンドルのリストに基づいて)その時点で1つの接続が開いていることを完全に確信していたので、本当に不思議でした。

System.Data.SQLite .Netプロバイダを使用してデータアクセスレイヤ全体を一から構築しました。私が計画したときに、接続やトランザクションに特別な注意を払い、取引はぶらぶらしていた。

難しい部分は、ExecuteNonQuery()コマンドでブレークポイントを設定し、アプリケーションをデバッグモードで実行するとエラーが消えることです。 グーグルで、私はこのサイトで興味深いものを見つけました:http://www.softperfect.com/board/read.php?8,5775。そこで、データベースパスをアンチウイルス無視リストに入れるように著者に提案したスレッドに返答しました。

データベースファイルを自分のウイルス対策ソフトウェア(Microsoft Security Essentials)の無視リストに追加し、問題を解決しました。これ以上のデータベースロックエラーはありません!

関連する問題