0

SQLiteデータベースを使用していて、それにレコードを挿入しています。これには非常に長い時間がかかります!1分間に数千を処理できると言う人を見たことがあります。私は約2400のレコードを持っています。各レコードの完了には30秒から2分かかります。データベースを再作成することはオプションではありません。私は1つのトランザクションをさまざまな方法で作成しようとしました。ProgressBar何かが起こっていることを示すためにを使用しているので、タイマーを使用する必要があります。これが私が使用しているコードです:

string con;
con = string.Format(@"Data Source={0}", documentsFolder);

SQLiteConnection sqlconnection = new SQLiteConnection(con);
SQLiteCommand sqlComm = sqlconnection.CreateCommand();
sqlconnection.Open();
SQLiteTransaction transaction = sqlconnection.BeginTransaction();

Timer timer2 = new Timer();
timer2.Interval = 1000;
timer2.Tick += (source, e) =>
                    {
                        URL u = firefox.URLs[count2];
                        string newtitle = u.title;
                        form.label1.Text = count2 + "/" + pBar.Maximum;
                        string c_urls = "insert or ignore into " + table + " (id,
 url, title, visit_count, typed_count, last_visit_time, hidden) values (" + dbID + ",'" + u.url + "','" 
    + newtitle + "',1,1, " + ToChromeTime(u.visited) + ", 0)";
                        string c_visited = "insert or ignore into " + table2 + " (id,
 url, 
    visit_time, transition) values (" + dbID2 + "," + dbID + "," + 
ToChromeTime(u.visited) + ",805306368)";
                        sqlComm = new SQLiteCommand(c_urls, sqlconnection);
                        sqlComm.ExecuteNonQuery();
                        sqlComm = new SQLiteCommand(c_visited, sqlconnection);
                        sqlComm.ExecuteNonQuery();

                        dbID++;
                        dbID2++;


                        pBar.Value = count2;
                        if (pBar.Maximum == count2)
                        {
                            pBar.Value = 0;
                            timer.Stop();
                            transaction.Commit();
                            sqlComm.Dispose();
                            sqlconnection.Dispose();
                            sqlconnection.Close();
                        }

                        count2++;
                    };
timer2.Start();

私は何が間違っているのですか?

4

3 に答える 3

2

これが私が順番に取り上げる内容です。それは問題を解決するかもしれないし、しないかもしれませんが、見るのに害はありません(そしてそれはただいくつかの魔法をするかもしれません):

  1. データベースが(別のスレッド、プロセス、さらにはタイマーからの)更新と競合していないことを確認してください。ライターはロックを取得し、クローズされていない/長時間実行されているトランザクションは悪い方法で相互作用する可能性があります。(「30秒から2分」かかる更新の場合、ロックの取得に問題があると思います。また、ローカルドライブなど、DBが存在するメディアが十分であることを確認してください。)

  2. トランザクションは使用されていません(??)。トランザクションをタイマーコールバックに移動し、適切なSQLCommandにアタッチして、コールバックが終了する前に破棄します。(使用using)。

  3. すべてのSQLCommandが正しく破棄されているわけではありません。一人一人を処分します。(を使用すると、usingこれが単純化されます。コールバックを超えてブリードしないでください。)

  4. プレースホルダーは使用されていません。これは、よりシンプルで使いやすいだけでなく、SQLiteとアダプターにとってもわずかに使いやすいものです。

(例のみ。次のコードでエラーが発生する可能性があります。)

// It's okay to keep long-running SQLite connections.
// In my applications I have a single application-wide connection.
// The more important thing is watching thread-access and transactions.
// In any case, we can keep this here.
SQLiteConnection sqlconnection = new SQLiteConnection(con);
sqlconnection.Open();

// In timer event - remember this is on the /UI/ thread.
// DO NOT ALLOW CROSS-THREAD ACCESS TO THE SAME SQLite CONNECTION.
// (You have been warned.)
URL u = firefox.URLs[count2];
string newtitle = u.title;
form.label1.Text = count2 + "/" + pBar.Maximum;

try {
   // This transaction is ONLY kept about for this timer callback.
   // Great care must be taken with long-running transactions in SQLite.
   // SQLite does not have good support for (long running) concurrent-writers
   // because it must obtain exclusive file locks.
   // There is no Table/Row locks!
   sqlconnection.BeginTransaction();
   // using ensures cmd will be Disposed as appropriate.
   using (var cmd = sqlconnection.CreateCommand()) {
     // Using placeholders is cleaner. It shouldn't be an issue to
     // re-create the SQLCommand because it can be cached in the adapter/driver
     // (although I could be wrong on this, anyway, it's not "this issue" here).
     cmd.CommandText = "insert or ignore into " + table
       + " (id, url, title, visit_count, typed_count, last_visit_time, hidden)"
       + " values (@dbID, @url, 'etc, add other parameters')";
     // Add each parameter; easy-peasy
     cmd.Parameters.Add("@dbID", dbID);
     cmd.Parameter.Add("@url", u.url);
     // .. add other parameters
     cmd.ExecuteNonQuery();
   }
   // Do same for other command (runs in the same TX)
   // Then commit TX
   sqlconnection.Commit();
} catch (Exception ex) {
   // Or fail TX and propagate exception ..
   sqlconnection.Rollback();
   throw;
}

if (pBar.Maximum == count2)
{
    pBar.Value = 0;
    timer.Stop();
    // All the other SQLite resources are already
    // cleaned up!
    sqlconnection.Dispose();
    sqlconnection.Close();
}
于 2012-07-19T23:33:54.930 に答える
2

これがあなたの問題かどうかはわかりませんが、ADO.NET を使用する一般的なパターンが間違っています。挿入ごとに新しいコマンドを作成しないでください (クエリの準備に繰り返し支払う必要があります)。

代わりに、次の操作を行います。

  • ループの前:
    • コマンドを一度作成します。
    • 適切なバインド パラメータを作成します。
  • ループの中:
    • バインドされたパラメーターに適切な値を割り当てるだけです。
    • そして、コマンドを実行します。

より粒度の低いトランザクションの使用を検討することもできます。トランザクションの耐久性に対する支払いを最小限に抑えるために、同じトランザクションに複数の挿入を入れてみてください。

この投稿もご覧になることをお勧めします。

于 2012-07-20T01:00:23.967 に答える