3

テーブルにデータを一括挿入するには、次の方法があります。まず、私のコードはデータテーブルにデータを入力し、.netのSqlBulkCopy句を使用して、このデータを対応するテーブルに挿入します。

データをすべてのテーブルに挿入するか、どちらにも挿入しないようにする必要があります。このために、私は.netのSqlTransactionクラスを使用しました。

シナリオは、複数のスレッドが同時に次のコードブロックを実行することです。

 public void Import()
        {
            using (SqlConnection sqlConnection = new SqlConnection(connectionString))
            {

                SqlTransaction sqlTrans =null;
                try
                {
                    sqlConnection.Open();
                    sqlTrans = sqlConnection.BeginTransaction(IsolationLevel.Serializable)


                    SqlCommand cmd = sqlConnection.CreateCommand();
                    cmd.CommandText = "select top 1 null from lockTable with(xlock)";
                    cmd.CommandTimeout = 3600*3;
                    cmd.Transaction = sqlTrans;
                    SqlDataReader reader = cmd.ExecuteReader();


                    foreach (DataTable dt in DataTables)
                    {
                        ImportIntoDatabase(sqlConnection, dt, sqlTrans);
                    }

                    reader.Close();
                    sqlTrans.Commit();                    
                }
                catch (Exception ex)
                {
                    sqlTrans.Rollback();
                    throw ex;
                }
            }
        }

       private void ImportIntoDatabase(SqlConnection sqlConn, DataTable dt, SqlTransaction sqlTrans)
        {
            using (SqlBulkCopy bulkCopy = new SqlBulkCopy(sqlConn, SqlBulkCopyOptions.Default, sqlTrans))
            {
                bulkCopy.BulkCopyTimeout = dt.Rows.Count * 10;
                try
                {
                    bulkCopy.DestinationTableName = dt.TableName;                    
                    bulkCopy.WriteToServer(dt);
                }
                catch (Exception ex)
                {
                   throw ex;
                }
            }
        }

この同時実行性を処理するために、もう1つのテーブル(一括挿入テーブル)が存在するデータベースに1つのダミーテーブル(「lockTable」という名前のテーブル)を作成しました。SqlTransactionのこのダミーテーブルで排他ロックが発生し、コマンドタイムアウトが3時間になります。

問題:次の例外が発生します

:宛先テーブル'Tbl1'にアクセスできません(tbl1は一括挿入用のテーブルです)

キャッチブロックでトランザクションをロールバックしている間、別の例外が続きます

:アクティビティの実行中にエラーが発生しましたサーバーはトランザクションを再開できませんでした。Desc:3a00000001。このセッションでアクティブなトランザクションは、別のセッションによってコミットまたは中止されました。

コードのこの奇妙な振る舞いについて誰かが私を助けてくれますか?私はすでにインターネットでこの問題についてたくさん検索しましたが、私にとって役立つものは何も見つかりませんでした。

4

3 に答える 3

2

インポート(DataTablesのDataTable dt)では、スレッドセーフにはなりません。

sqlConnectionには既にImportからのアクティブなリーダーがあるため、ImportIntoDatabaseで接続を使用することはできません。

Echo smp-テーブルをロックしているのなら、なぜマルチスレッドなのか?

SQL挿入の実行中に入力を作成する場合は、SqlCommand.BeginExecuteReaderなどのAsynchメソッドを使用します。スレッドのオーバーヘッドなしで非同期を取得します。また、DataTablesは比較的低速です。TVPと軽量オブジェクトを使用して挿入します。挿入パフォーマンスの大きな要因は、インデックスの断片化です。可能であれば、クラスター化されたインデックスの順序で順序を挿入します。ループは単純なビルド入力であり、非同期を待ち、asychを実行します。または、ビルド入力をキューからの読み取り入力にすることもできます。同じテーブルへのSQL挿入は、通常、並行して高速化することはありません。私の経験では、インサート間に時間差のないシリアルインサートを注文しました。

于 2012-07-10T13:23:19.227 に答える
1

問題を解決しました。

以下は、インポートメソッドに加えた変更です。

public void Import()
        {
            using (SqlConnection sqlConnection = new SqlConnection(connectionString))
            {
                sqlConnection.Open();
                using (SqlTransaction sqlTrans = sqlConnection.BeginTransaction())
                {
                    try
                    {                       
                        SqlCommand cmd = sqlConnection.CreateCommand();
                        cmd.CommandText = "select top 1 null from lockTable with(xlock)";
                        cmd.CommandTimeout = LOCK_TIME_OUT;
                        cmd.Transaction = sqlTrans;
                        SqlDataReader reader = cmd.ExecuteReader();


                        foreach (DataTable dt in DataTables)
                        {
                            ImportIntoDatabase(sqlConnection, dt, sqlTrans);                            
                        }

                        reader.Close();
                        sqlTrans.Commit();                        
                    }
                    catch (Exception ex)
                    {
                        sqlTrans.Rollback();
                        throw ex;
                    }
                }
                sqlConnection.Close();
            }
        }
于 2012-07-16T10:02:44.553 に答える
0

複数のスレッドが「インポート」メソッドにアクセスできる場合、このメソッドのコンテンツをロックするべきではありませんか?

ダミーテーブルは必要ないと思います。上記の2つのメソッドをロックするだけです。

また、すべてのスレッドを結合して、スレッドがいつ終了したかがわかるようにする必要があることにも言及します。

于 2012-07-10T07:55:28.133 に答える