gpt4 book ai didi

c# - 使用相同的 SqlConnection 对 SqlCommand.BeginExecuteNonQuery 的多个并发调用

转载 作者:IT王子 更新时间:2023-10-29 04:14:52 25 4
gpt4 key购买 nike

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

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

我发现了用计算结果更新临时表的性能瓶颈。此代码已对更新进行批处理,以防止 C# 客户端内存不足。每批计算数据都通过 SqlCommand.ExecuteNonQuery 发送到存储过程,存储过程依次更新临时表。代码大部分时间都花在调用 ExecuteNonQuery 上。

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

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

抱歉,解释得太长了。

MSDN 文档状态:

The BeginExecuteNonQuery method returns immediately, but until the code executes the corresponding EndExecuteNonQuery method call, it must not execute any other calls that start a synchronous or asynchronous execution against the same SqlCommand object.

这似乎意味着不同的 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();
}
}

最佳答案

好吧,冒着收到大量反对票的极端风险,我不得不对此发表评论。首先,这是一个很好的问题,并且很好地解决了您提到的特定潜在问题。但是,您忽略了讨论您试图完成的这个“冗长”过程。

我的经历让我想到了一件事......

If the question your asking is hard to answer, change the question.

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

Ask yourself "Can you avoid it all?"

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

例如,您有一个大约 2GB 原始数据的数据库。您想要针对整个数据集生成复杂的报告或分析。好吧,简单地说,2gb 的内存很容易获得,使用字典或任何创建您需要的查找的方式将整个数据库(或您需要的部分)放入内存。取决于几个因素,整个事情的运行速度可能比 SQL 快几倍,可以很容易地进行单元测试,并且(恕我直言)比构建动态 SQL 的讨厌的存储过程更容易构建、调试和维护。即使有超过 2gb 的原始数据,也可以使用多种现有技术(B 树、ISAM 等)轻松创建客户端缓存。

我今天工作的产品在数据库中有 2.4tb 的数据,我们没有一个存储过程、连接语句,甚至没有一个不相等的 where 子句。

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

"Am I asking the right question?"

关于c# - 使用相同的 SqlConnection 对 SqlCommand.BeginExecuteNonQuery 的多个并发调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6374911/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com