首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >使用相同的SqlCommand.BeginExecuteNonQuery对SqlConnection进行多次并发调用

使用相同的SqlCommand.BeginExecuteNonQuery对SqlConnection进行多次并发调用
EN

Stack Overflow用户
提问于 2011-06-16 16:01:04
回答 8查看 16.5K关注 0票数 30

我有一些工作的C#代码,这些代码使用SqlConnection创建临时表(例如#Foo),调用存储的procs来填充这些临时表并将结果返回给C#客户端,使用c#对这些结果执行复杂的计算,并使用计算结果更新前面创建的一个临时表。

由于整个过程中使用的临时表,我们必须只有一个SqlConnection。

在用计算结果更新临时表时,我发现了一个性能瓶颈。这段代码已经在批处理更新,以防止C#客户端耗尽内存。每一批计算数据都通过SqlCommand.ExecuteNonQuery发送到存储的proc,而sproc依次更新临时表。代码在调用ExecuteNonQuery时花费了大部分时间。

因此,我将其更改为BeginExecuteNonQuery,以及等待线程并调用EndExecuteNonQuery的代码。这提高了大约三分之一的性能,但我担心使用同一个SqlConnection对SqlCommand.BeginExecuteNonQuery进行多个并发调用。

这样可以吗,还是会遇到线程问题?

抱歉解释得太长了。

MSDN文档状态:

BeginExecuteNonQuery方法会立即返回,但是在代码执行相应的EndExecuteNonQuery方法调用之前,它不能对同一个SqlCommand对象执行任何启动同步或异步执行的其他调用。

这似乎意味着不同的SqlCommand对象可以在第一个SqlCommand完成之前调用BeginExecuteNonQuery。

下面是一些说明问题的代码:

代码语言:javascript
代码运行次数:0
运行
复制
    private class SqlCommandData
    {
        public SqlCommand Command { get; set; }
        public IAsyncResult AsyncResult { get; set; }
    }

    public static void TestMultipleConcurrentBeginExecuteNonQueryCalls(string baseConnectionString)
    {
        var connectionStringBuilder = new SqlConnectionStringBuilder(baseConnectionString)
                                          {
                                              MultipleActiveResultSets = true,
                                              AsynchronousProcessing = true
                                          };
        using (var connection = new SqlConnection(connectionStringBuilder.ConnectionString))
        {
            connection.Open();

            // ELIDED - code that uses connection to do various Sql work

            SqlDataReader dataReader = null;
                // in real code, this would be initialized from calls to SqlCommand.ExecuteReader, using same connection

            var commandDatas = new List<SqlCommandData>();
            var count = 0;
            const int maxCountPerJob = 10000;
            while (dataReader.Read())
            {
                count++;
                // ELIDED - do some calculations on data, too complex to do in SQL stored proc
                if (count >= maxCountPerJob)
                {
                    count = 0;
                    var commandData = new SqlCommandData
                                          {
                                              Command = new SqlCommand {Connection = connection}
                                          };
                    // ELIDED - other initialization of command - used to send the results of calculation back to DB
                    commandData.AsyncResult = commandData.Command.BeginExecuteNonQuery();
                    commandDatas.Add(commandData);
                }
            }
            dataReader.Close();

            WaitHandle.WaitAll(commandDatas.Select(c => c.AsyncResult.AsyncWaitHandle).ToArray());
            foreach (var commandData in commandDatas)
            {
                commandData.Command.EndExecuteNonQuery(commandData.AsyncResult);
                commandData.Command.Dispose();
            }

            // ELIDED - more code using same SqlConnection to do final work

            connection.Close();
        }
    }
EN

回答 8

Stack Overflow用户

发布于 2011-07-07 22:40:46

嗯,尽管极有可能获得大量的选票,但我不得不对此发表评论。首先,这是一个很好的问题,很好地阐述了你提到的具体的潜在问题。然而,你忽略了讨论这个你想要完成的“漫长”过程。

我的经验让我想到一件事..。

如果你问的问题很难回答,就改变这个问题。

虽然我对你的具体问题知之甚少,但我认为这完全适用于你的困境。正如其他人提到的..。临时表令人讨厌,为特定任务创建自己的表更困难,在SQL中更新大量数据是很昂贵的。

问问自己:“你能避免这一切吗?”

人们常常选择在数据库中实现极其复杂的逻辑,因为他们相信SQL可以更快地实现它。实际上,这是一个有缺陷的概念,数据库是存储/序列化设备,它们擅长存储、更新、定位和同步访问数据。他们没有很好的设备来处理复杂的行动。即使在Microsoft(和其他人)通过向数据库中注入完整的开发语言来实现数据库的私生子化之后,它也不能像编写得很好的客户端那样最优地执行(*取决于操作的复杂性,我想您已经超过了这一点)。

例如,您有一个包含大约2gb的原始数据的数据库。您希望对整个数据集生成复杂的报告或分析。简单地说,2gb的内存是很容易获得的,使用字典或任何东西来创建您需要的查找,将整个数据库(或您需要的部分)读入内存。取决于几个因素,整个程序的运行速度可能比SQL快几倍,可以很容易地进行单元测试,并且(IMHO)比构建动态SQL的各种SPROCs更容易构建、调试和维护。即使有超过2gb的原始数据,客户端缓存也可以很容易地使用现有的几种技术(B树、ISAM等)创建。

我今天使用的产品在数据库中有2.4tb的数据,而且我们没有一个sproc、join语句,甚至没有一个不相等的where子句。

但遗憾的是,我的建议可能与你的具体情况相关,也可能与你的具体情况无关,因为我不知道你的目标或限制。希望,如果没有别的,它会让你问自己:

“我问的问题对吗?”

票数 17
EN

Stack Overflow用户

发布于 2011-06-28 20:39:44

您可以使用具有两个线程和两个同时但独立的sql连接的生产者-使用者模式。

生产者(第一个线程)具有DataReader (第一个sql连接),并将其结果写入阻塞队列。使用者(第二个线程)从队列中读取,具有ExecuteNonQuery (第二个sql连接)并写入临时表。

在ExecuteNonQuery命令基本上是多个插入的情况下,另一个想法是:ExecuteNonQuery具有带有StringCollection的重载作为一个操作发送多个sql语句。

票数 3
EN

Stack Overflow用户

发布于 2011-07-11 00:43:41

只能有一个与命令对象相关联的DataReader,也可以有多个与同一连接相关联的命令对象。这里唯一不能做的事情是使用具有不同参数的相同命令。

但是,当您启动数据库事务时(如果不是显式的话),与该事务关联的资源将被锁定,直到事务被提交或回滚,并且所有希望查询这些资源的进程都被放入队列中。Server很好地管理队列。由于SQL server 2000中服务器负载高,我在死锁方面遇到了一些问题,但是在以后的版本中没有出现这样的问题。

奇怪的是,您实际上得到了性能改进。这使我认为您有大量的数据,在发送到Server时需要时间进行处理。在发送块时,由于同时执行数据传输和数据处理,因此消耗的时间较少。

不管怎样,这不应该有任何问题。

但是,考虑使用CLR程序集(如果有此选项)直接在数据库引擎中处理信息,而不需要TCP通信量。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/6374911

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档