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

2024-05-02

我有一些可用的 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(使用前将#替换为@)

使用相同的 SqlConnection 对 SqlCommand.BeginExecuteNonQuery 进行多个并发调用 的相关文章

  • 通过 SOAP 的 Gmt php 或 UTC C# 等效项

    is C DateTime UtcNow和 PHPdate c 是等价的 我怀疑 因为当我肥皂时 我得到了 C
  • C# 和月历,选择多个日期

    我正在制作一个程序 可以帮助人们用 C 为某个部门 预订 订单 他们需要能够选择不同月份的多个日期 我更愿意拥有它 这样他们就可以单击一个日期 然后按住 Shift 键单击另一个日期以选择这两个日期之间的所有日期 并控制单击以进行单选 取消
  • 如何使用 C# 以编程方式编辑 Power BI Desktop 文档参数或数据源?

    我有一个在 Power BI Desktop 中内置的报告模板 并保存为 pbix 或 pbit 文件 该模板使用DirectQuery SQL数据库作为数据源 而服务器地址和数据库名称被提取到参数中 还有一个参数包含一个ReportId
  • 合并sql中的列

    我正在使用 SQL Server 2017 有一个存储过程 其中我有一个带有连接的简单选择 例如 SELECT p legacyKey AS JobNumber p Name AS JobName G Label AS DesignStat
  • 如何调试在发布版本中优化的变量

    我用的是VS2010 我的调试版本工作正常 但我的发布版本不断崩溃 因此 在发布版本模式下 我右键单击该项目 选择 调试 然后选择 启动新实例 此时我看到我声明的一个数组 int ma 4 1 2 8 4 永远不会被初始化 关于可能发生的事
  • C# Winforms Designer 无法打开,因为它无法在同一程序集中找到类型

    我收到以下错误 找不到类型 My Special UserControl 请确保引用包含此类型的程序集 如果此类型是您的开发项目的一部分 请确保已使用当前平台或任何 CPU 的设置成功构建该项目 但没有任何意义的是My Special Us
  • C 类型命名约定,_t 或 ALLCAPS

    我一直想知道是否有任何命名约定 例如何时对类型使用全部大写以及何时追加 t 什么时候不使用任何东西 我知道当时 K R 发布了各种有关如何使用 C 的文档 但我找不到任何相关内容 在 C 标准库类型中 t看起来漂亮占主导地位 time t
  • 名称查找、实例化点 (POI) 和基本类型

    以下代码针对 X 进行编译 但不适用于 double struct X void foo double void foo X namespace NN struct A void foo A foo double error foo not
  • “没有合适的默认构造函数可用”——为什么会调用默认构造函数?

    我已经查看了与此相关的其他一些问题 但我不明白为什么在我的情况下甚至应该调用默认构造函数 我可以只提供一个默认构造函数 但我想了解它为什么这样做以及它会产生什么影响 error C2512 CubeGeometry no appropria
  • 编写具有多种类型的泛型扩展方法时的类型推断问题

    我正在为 IEnumerable 编写一个通用扩展方法 用于将对象列表映射到另一个映射对象列表 这就是我希望该方法的工作方式 IList
  • 在 C 语言中替换宏内的宏

    我正在尝试使代码部分可重用 我下面的评论片段没有达到我想要的效果 define NAME ABC define LOG SIZE NAME LEN 我想LOG SIZE决心ABC LEN 我尝试过使用 但没能让它发挥作用 LOG SIZE在
  • 在 C++ 代码 gdb 中回溯指针

    我在运行 C 应用程序时遇到段错误 在 gdb 中 它显示我的一个指针位置已损坏 但我在应用程序期间创建了 10 万个这样的对象指针 我怎样才能看到导致崩溃的一个 我可以在 bt 命令中执行任何操作来查看该指针的生命周期吗 谢谢 鲁奇 据我
  • 当我使用可变参数而不是常量参数时,为什么我的内联表 UDF 慢得多?

    我有一个表值内联 UDF 我想过滤该 UDF 的结果以获得一个特定值 当我使用常量参数指定过滤器时 一切都很好 并且性能几乎是瞬时的 当我使用可变参数指定过滤器时 它会花费明显更大的时间块 大约是逻辑读取的 500 倍和持续时间的 20 倍
  • WPF DataGrid - 在每行末尾添加按钮

    我想在数据网格的每一行的末尾添加一个按钮 我找到了以下 xaml 但它将按钮添加到开头 有人知道如何在所有数据绑定列之后添加它吗 这会将按钮添加到开头而不是末尾
  • 使用 Unity 在 C# 中发送 http 请求

    如何使用 Unity 在 C 中发送 HTTP GET 和 POST 请求 我想要的是 在post请求中发送json数据 我使用Unity序列化器 所以不需要 新的 我只想在发布数据中传递一个字符串并且能够 将 ContentType 设置
  • 不使用放置 new 返回的指针时的 C++ 严格别名

    这可能会导致未定义的行为吗 uint8 t storage 4 We assume storage is properly aligned here int32 t intPtr new void storage int32 t 4 I k
  • 值和类型的简洁双向静态 1:1 映射

    我将从我想象如何使用我想要创建的代码开始 它不必完全像这样 但它是我在标题中所说的 简洁 的一个很好的例子 就我而言 它是将类型映射到相关的枚举值 struct bar foo
  • 使用 boost 异步发送和接收自定义数据包?

    我正在尝试使用 boost 异步发送和接收自定义数据包 根据我当前的实现 我有一些问题 tcpclient cpp include tcpclient h include
  • MSVC编译器下使用最大成员初始化联合

    我正在尝试初始化一个LARGE INTEGER在 C 库中为 0 确切地说是 C 03 以前 初始化是 static LARGE INTEGER freq 0 在 MinGW 下它产生了一个警告 缺少成员 LARGE INTEGER Hig
  • Emacs C++,打开相应的头文件

    我是 emacs 新手 我想知道 是否有在头文件 源文件和相应的源文件 头文件之间切换的快捷方式 是否有像通用 emacs 参考卡那样的参考卡 Thanks There s ff find other file 您可以使用以下方法将其绑定到

随机推荐