我有一些可用的 C# 代码,它使用 SqlConnection 创建临时表(例如 #Foo),调用存储过程来填充这些临时表并将结果返回到 C# 客户端,使用 C# 对这些结果执行复杂的计算,并使用计算结果更新之前创建的临时表之一。
由于整个过程中都会使用临时表,因此我们必须只有一个 SqlConnection。
我发现使用计算结果更新临时表时存在性能瓶颈。此代码已经批量更新以防止 C# 客户端内存不足。每批计算的数据都通过 SqlCommand.ExecuteNonQuery 发送到存储过程,存储过程依次更新临时表。该代码大部分时间都花在对 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();
}
}
好吧,冒着收到大量反对票的极端风险,我必须对此发表评论。首先,这是一个很好的问题,并且很好地解决了您提到的具体潜在问题。然而,您忽略了讨论您试图完成的这个“漫长”的过程。
我的经历告诉我一件事......
如果您提出的问题很难回答,请更改问题。
虽然我对你的具体问题知之甚少,但我认为这完全适用于你的困境。正如其他人提到的...临时表很糟糕,为特定任务创建自己的表更糟糕,在 SQL 中更新大量数据的成本很高。
问问自己“你能避免这一切吗?”
人们常常选择在数据库中实现极其复杂的逻辑,因为他们相信 SQL 可以更快地完成它。实际上这是一个有缺陷的概念,数据库是存储/序列化设备,它们擅长存储、更新、定位和同步对数据的访问。它们没有足够的能力来处理复杂的操作。即使在微软(和其他人)通过向数据库注入完整的开发语言来对数据库进行混蛋之后,它也无法像编写良好的客户端一样优化地执行(*取决于操作的复杂性,我怀疑您已经超越了)。
例如,您有一个包含大约 2GB 原始数据的数据库。您想要对整个数据集生成复杂的报告或分析。简单地说,2GB 内存很容易获得,使用字典或其他任何方式将整个数据库(或您需要的部分)放入内存中以创建您需要的查找。根据几个因素,整个事情的运行速度可能比 SQL 快几倍,可以轻松进行单元测试,并且(恕我直言)比构建动态 SQL 的各种存储过程要容易得多(恕我直言)。即使原始数据超过 2GB,也可以使用多种现有技术(B 树、ISAM 等)轻松创建客户端缓存。
我今天开发的产品数据库中有 2.4tb 的数据,而且我们没有一个存储过程、join 语句,甚至没有一个非等式的 where 子句。
但遗憾的是,我的建议可能适合也可能不适合您的具体情况,因为我不知道您的目标或限制。希望,如果没有别的事,它会让你问自己:
“我问的问题正确吗?”
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)