我有一些工作的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。
下面是一些说明问题的代码:
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();
}
}
发布于 2011-07-07 14:40:46
嗯,尽管极有可能获得大量的选票,但我不得不对此发表评论。首先,这是一个很好的问题,很好地阐述了你提到的具体的潜在问题。然而,你忽略了讨论这个你想要完成的“漫长”过程。
我的经验让我想到一件事..。
如果你问的问题很难回答,就改变这个问题。
虽然我对你的具体问题知之甚少,但我认为这完全适用于你的困境。正如其他人提到的..。临时表令人讨厌,为特定任务创建自己的表更困难,在SQL中更新大量数据是很昂贵的。
问问自己:“你能避免这一切吗?”
人们常常选择在数据库中实现极其复杂的逻辑,因为他们相信SQL可以更快地实现它。实际上,这是一个有缺陷的概念,数据库是存储/序列化设备,它们擅长存储、更新、定位和同步访问数据。他们没有很好的设备来处理复杂的行动。即使在Microsoft(和其他人)通过向数据库中注入完整的开发语言来实现数据库的私生子化之后,它也不能像编写得很好的客户端那样最优地执行(*取决于操作的复杂性,我想您已经超过了这一点)。
例如,您有一个包含大约2gb的原始数据的数据库。您希望对整个数据集生成复杂的报告或分析。简单地说,2gb的内存是很容易获得的,使用字典或任何东西来创建您需要的查找,将整个数据库(或您需要的部分)读入内存。取决于几个因素,整个程序的运行速度可能比SQL快几倍,可以很容易地进行单元测试,并且(IMHO)比构建动态SQL的各种SPROCs更容易构建、调试和维护。即使有超过2gb的原始数据,客户端缓存也可以很容易地使用现有的几种技术(B树、ISAM等)创建。
我今天使用的产品在数据库中有2.4tb的数据,而且我们没有一个sproc、join语句,甚至没有一个不相等的where子句。
但遗憾的是,我的建议可能与你的具体情况相关,也可能与你的具体情况无关,因为我不知道你的目标或限制。希望,如果没有别的,它会让你问自己:
“我问的问题对吗?”
发布于 2011-06-28 12:39:44
您可以使用具有两个线程和两个同时但独立的sql连接的生产者-使用者模式。
生产者(第一个线程)具有DataReader (第一个sql连接),并将其结果写入阻塞队列。使用者(第二个线程)从队列中读取,具有ExecuteNonQuery (第二个sql连接)并写入临时表。
在ExecuteNonQuery命令基本上是多个插入的情况下,另一个想法是:ExecuteNonQuery具有带有StringCollection的重载作为一个操作发送多个sql语句。
发布于 2011-07-10 16:43:41
只能有一个与命令对象相关联的DataReader,也可以有多个与同一连接相关联的命令对象。这里唯一不能做的事情是使用具有不同参数的相同命令。
但是,当您启动数据库事务时(如果不是显式的话),与该事务关联的资源将被锁定,直到事务被提交或回滚,并且所有希望查询这些资源的进程都被放入队列中。Server很好地管理队列。由于SQL server 2000中服务器负载高,我在死锁方面遇到了一些问题,但是在以后的版本中没有出现这样的问题。
奇怪的是,您实际上得到了性能改进。这使我认为您有大量的数据,在发送到Server时需要时间进行处理。在发送块时,由于同时执行数据传输和数据处理,因此消耗的时间较少。
不管怎样,这不应该有任何问题。
但是,考虑使用CLR程序集(如果有此选项)直接在数据库引擎中处理信息,而不需要TCP通信量。
https://stackoverflow.com/questions/6374911
复制相似问题