3

我有以下方法在表中批量插入数据。首先,我的代码填充数据表中的数据,并使用 .net 的 SqlBulkCopy claas 将此数据插入相应的表中。

我要求数据应该插入所有表中,或者都不插入。为此,我使用了 .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;
                }
            }
        }

为了处理这种并发,我在另一个表所在的数据库(批量插入表)中创建了一个虚拟表(名为“lockTable”的表)。我在 SqlTransaction 中的这个虚拟表上获得了排他锁,命令超时时间高达 3 小时。

问题:我收到以下异常

: 无法访问目标表 'Tbl1'(tbl1 是用于批量插入的表)

紧随其后的是另一个异常,同时在 catch 块中回滚事务

:执行活动时出错服务器无法恢复事务。描述:3a00000001。此会话中活动的事务已被另一个会话提交或中止。

任何人都可以帮助我解决代码的这种奇怪行为。我已经在互联网上搜索了很多关于这个问题的信息,但我没有发现任何对我有帮助的东西。

4

3 回答 3

2

在导入中(DataTables 中的 DataTable dt)不会是线程安全的。

sqlConnection 已经有一个来自 Import 的活动读取器,因此无法在 ImportIntoDatabase 中使用连接。

Echo smp - 如果您要锁定表,那么为什么要使用多线程?

如果您想在 SQL 插入发生时构建输入,请使用异步方法,例如 SqlCommand.BeginExecuteReader。您可以在没有线程开销的情况下获得异步。并且 DataTables 相对较慢。我使用 TVP 和轻量级对象插入。插入性能的一个重要因素是索引碎片。如果可能按聚集索引的顺序插入顺序。循环是简单的构建输入,等待异步,运行异步。或者构建输入可以从队列中读取输入。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

如果多个线程可以访问“导入”方法,那么您不应该锁定该方法的内容吗?

我不认为你需要一个虚拟表,你只需要锁定上面的两种方法。

我还要提到你应该加入所有线程,这样你就可以知道它们何时完成。

于 2012-07-10T07:55:28.133 回答