none
BackgroundWorker内のSqlCommandでタイムアウト RRS feed

  • 質問

  • 環境:VisualStudio 2005 .Net C# + SqlServer 2000

    お世話になります。

    数百万件のテーブルが5つあり、これらを読み込んである処理をするのに

    1テーブル当たり数十分かかります。

    そこで、時間短縮のために、BackgroundWorkerを5つ用意して(接続も5つ)、

    同時にテーブルを読み込もうとしたところ、4つのBackgroundWorkerでタイムアウトが発生しました。

    何がいけないのかアドバイスをいただけないでしょうか。

    -----

      SqlConnection[] sql_conn = new SqlConnection[4 + 1];

    //sql_conn[0]~[5]すべてオープンする処理

      NpgsqlConnection[] pg_conn = new NpgsqlConnection[4 + 1];

    //pg_conn[0]~[5]すべてオープンする処理

      BackgroundWorker[] bgw = new BackgroundWorker[4 + 1];

        //
        // 優先テーブル待ち合わせ
        //
        bgw[0].RunWorkerAsync(new DoWorkParam(sql_conn[0], pg_conn[0],100));
        while (bgw[0].IsBusy)
        {
               Thread.Sleep(100);
               Application.DoEvents();
        }

        //
        // BigTables 待ち合わせ
        //
        for (int i = 1; i <= 4; i++)
        {
              bgw[i].RunWorkerAsync(new DoWorkParam(sql_conn[i], pg_conn[i], 900+i));
        }
        bool bBusy = true;
        while (bBusy)
        {
              Thread.Sleep(100);
              Application.DoEvents();

              bBusy = false;
              for (int i = 1; i <= 4; i++)
              {
                     bBusy = bBusy || bgw[i].IsBusy;
              }
        }

    -----

    private void bgw_DoWork(object sender, DoWorkEventArgs e)
    {

        // このメソッドへのパラメータ
            DoWorkParam bgWorkerArg = (DoWorkParam)e.Argument;

        try
            {
                            
                    table = this.MainList.Rows[idx]["テーブル名"].ToString();

                    //テーブル単位でトランザクション
                    trn = bgWorkerArg.pg_conn.BeginTransaction();

                    try
                    {
                                // 件数取得
                                using (SqlCommand cmdCount = new SqlCommand(

    string.Format("SELECT COUNT(*) FROM {0}", table), bgWorkerArg.sql_conn))
                                {
                                    this.MainList.Rows[idx]["件数"] = Convert.ToInt64(cmdCount.ExecuteScalar());
                                }
                            }
                            catch (Exception ex)
                            {
                                //ロールバック
                                if (trn != null)
                                {
                                    trn.Rollback();
                                    trn.Dispose();
                                    trn = null;
                                }

                                ret = false;
                                continue;
                            }

                            try
                            {
                                using (SqlCommand cmd = bgWorkerArg.sql_conn.CreateCommand())
                                {
                                    cmd.CommandText = string.Format("SELECT * FROM {0}", table);
                                    using (SqlDataReader sqlrd = cmd.ExecuteReader())
                                    {

                                        while (sqlrd.Read())
                                        {

                                         //いろいろ処理が続く

    ---
        public class DoWorkParam
        {

            public SqlConnection sql_conn;
            public NpgsqlConnection pg_conn;
            public int group;
            public DoWorkParam()
            {

            }
            public DoWorkParam(SqlConnection sql_conn, NpgsqlConnection pg_conn, int group)
            {
                this.sql_conn = sql_conn;
                this.pg_conn = pg_conn;
                this.group = group;
            }
        }

    ---

     

     

     

    2010年6月10日 10:02

回答

  • 数百万件のデータ全てをSELECT文でプログラム側に転送してから処理しているのでしょうか?
    使用メモリなどのも気になります。

    処理内容にもよりますが、ストアドプロシージャを使用してSQL Server内で処理した方が、データの転送コストも省略できますし、DBエンジンのメモリ内で処理できることにより、より高速な処理が期待できます。

    • 回答の候補に設定 山本春海 2010年6月22日 7:35
    • 回答としてマーク 山本春海 2010年6月29日 6:52
    2010年6月10日 12:02
  • ただし、処理速度が遅いため(バッチなのでシビアではないのですが)改良しようとしています。「ある処理」と記載しましたが、SQLServerのデータを読み込んでPosgtreSQLにデータコピーしたいのです。

    こういった処理には SSIS が向いているように思いますが、それはさておき。

    そこで、時間短縮のために、BackgroundWorkerを5つ用意して(接続も5つ)、
    同時にテーブルを読み込もうとしたところ、4つのBackgroundWorkerでタイムアウトが発生しました。

    DB サーバーやクライアント PC のリソース(CPU とかメモリとかネットワークとか)は十分なのでしょうか。
    BackgroundWorker を同時に走らせると重たい処理がいっぺんに動くことになるので、ひとつの Command の処理時間は長くなります。
    コマンドの実行時間が長くなるのであれば、CommandTimeout を適切な時間に調整することも選択肢かと。
    (SqlCommand のタイムアウトなのでデッドロックを真っ先に疑うところですが、テスト プログラムでその可能性はないことを確認してますよね。)
    • 回答の候補に設定 山本春海 2010年6月22日 7:35
    • 回答としてマーク 山本春海 2010年6月29日 6:52
    2010年6月11日 3:38

すべての返信

  • 数百万件のデータ全てをSELECT文でプログラム側に転送してから処理しているのでしょうか?
    使用メモリなどのも気になります。

    処理内容にもよりますが、ストアドプロシージャを使用してSQL Server内で処理した方が、データの転送コストも省略できますし、DBエンジンのメモリ内で処理できることにより、より高速な処理が期待できます。

    • 回答の候補に設定 山本春海 2010年6月22日 7:35
    • 回答としてマーク 山本春海 2010年6月29日 6:52
    2010年6月10日 12:02
  • 佐祐理さん、前回は「分割してFill?・・・」の件でお世話になりました。

    アドバイスをいただき、DbCommand+DbDataReaderで対処することで

    OutofMemoryExceptionが回避できました。その節はありがとうございます。

    ただし、処理速度が遅いため(バッチなのでシビアではないのですが)改良しようとしています。

    「ある処理」と記載しましたが、SQLServerのデータを読み込んでPosgtreSQLにデータコピーしたいのです。

    ストアドで実現可能でしょうか?

    また、実際の実現方法とは別に、タイムアウトが発生する理由も知っておきたいです。

    小さいテーブルを5つ用意し、BackgroundWorkerを5つ用意して

    同様のテストプログラムを作った場合はうまく動作しました。

    原因がお分かりになりましたら教えてください。

    2010年6月11日 1:45
  • ただし、処理速度が遅いため(バッチなのでシビアではないのですが)改良しようとしています。「ある処理」と記載しましたが、SQLServerのデータを読み込んでPosgtreSQLにデータコピーしたいのです。

    こういった処理には SSIS が向いているように思いますが、それはさておき。

    そこで、時間短縮のために、BackgroundWorkerを5つ用意して(接続も5つ)、
    同時にテーブルを読み込もうとしたところ、4つのBackgroundWorkerでタイムアウトが発生しました。

    DB サーバーやクライアント PC のリソース(CPU とかメモリとかネットワークとか)は十分なのでしょうか。
    BackgroundWorker を同時に走らせると重たい処理がいっぺんに動くことになるので、ひとつの Command の処理時間は長くなります。
    コマンドの実行時間が長くなるのであれば、CommandTimeout を適切な時間に調整することも選択肢かと。
    (SqlCommand のタイムアウトなのでデッドロックを真っ先に疑うところですが、テスト プログラムでその可能性はないことを確認してますよね。)
    • 回答の候補に設定 山本春海 2010年6月22日 7:35
    • 回答としてマーク 山本春海 2010年6月29日 6:52
    2010年6月11日 3:38