bcp/BULK INSERT 与表值参数的性能

标签 performance sql-server-2008 bulkinsert table-valued-parameters

我将不得不使用 SQL Server 的 BULK INSERT 命令重写一些相当旧的代码,因为架构已更改,我突然想到也许我应该考虑改用 TVP 切换到存储过程,但我想知道是什么它可能对性能产生影响。

一些可能有助于解释为什么我问这个问题的背景信息:

  • 数据实际上是通过网络服务传入的。 Web 服务将一个文本文件写入数据库服务器上的共享文件夹,然后执行 BULK INSERT 。这个过程最初是在SQL Server 2000上实现的,当时除了把几百条INSERT语句扔到服务器上真的别无选择,这实际上是原始过程,是性能灾难。
  • 数据被批量插入到永久临时表中,然后合并到一个更大的表中(之后从临时表中删除)。
  • 要插入的数据量“大”,但不是“大”——通常是几百行,在极少数情况下可能是 5-10k 行。因此,我的直觉是 BULK INSERT 作为非日志操作不会产生太大的差异(但当然我不确定,因此问题)。
  • 插入实际上是一个更大的流水线批处理过程的一部分,需要连续发生多次;因此性能 关键。

  • 我想用 TVP 替换 BULK INSERT 的原因是:
  • 通过 NetBIOS 编写文本文件可能已经花费了一些时间,而且从架构的角度来看,这非常可怕。
  • 我相信可以(并且应该)消除临时表。它存在的主要原因是插入的数据需要在插入的同时用于其他几个更新,并且尝试从大量生产表进行更新比使用几乎为空的暂存要昂贵得多 table 。对于 TVP,参数基本上 临时表,我可以在主插入之前/之后用它做任何我想做的事情。
  • 我几乎可以消除重复检查、清理代码以及与批量插入相关的所有开销。
  • 如果服务器一次获得一些这样的事务,则无需担心临时表或临时数据库上的锁争用(我们试图避免它,但它发生了)。

  • 我显然会在将任何东西投入生产之前对此进行分析,但我认为在我花费所有时间之前先询问一下可能是个好主意,看看是否有人对为此目的使用 TVP 发出任何严厉警告。

    所以 - 对于任何对 SQL Server 2008 足够熟悉并尝试或至少调查过这个的人来说,结论是什么?对于插入,比如说,几百到几千行,发生的频率相当高,TVP 会切芥末吗?与批量插入相比,性能是否有显着差异?

    更新:现在问号减少了 92%!

    (又名:测试结果)

    经过 36 个阶段的部署过程,最终结果现已投入生产。这两种解决方案都经过了广泛的测试:
  • 撕掉共享文件夹代码,直接使用SqlBulkCopy类;
  • 切换到带有 TVP 的存储过程。

  • 为了让读者了解究竟测试了什么,消除对这些数据可靠性的怀疑,这里更详细地解释了这个导入过程的实际作用:
  • 从一个时间数据序列开始,通常大约有 20-50 个数据点(尽管有时可能高达几百个);
  • 对它进行一大堆疯狂的处理,这些处理大多独立于数据库。这个过程是并行的,所以(1)中大约有 8-10 个序列同时被处理。每个并行进程生成 3 个附加序列。
  • 将所有3个序列和原始序列合并成一个batch。
  • 将所有 8-10 个现已完成的处理任务的批次合并为一个大的 super 批次。
  • 使用 BULK INSERT 策略(参见下一步)或 TVP 策略(跳至步骤 8)导入它。
  • 使用 SqlBulkCopy 类将整个 super 批次转储到 4 个永久临时表中。
  • 运行一个存储过程,它 (a) 对 2 个表执行一系列聚合步骤,包括几个 JOIN 条件,然后 (b) 使用聚合数据和非聚合数据在 6 个生产表上执行 MERGE。 (完成的)

  • 生成 4 个 DataTable 包含待合并数据的对象;其中 3 个包含 CLR 类型,不幸的是 ADO.NET TVP 没有正确支持这些类型,因此必须将它们作为字符串表示形式放入,这会稍微影响性能。
  • 将 TVP 馈送到存储过程,该过程与 (7) 进行基本相同的处理,但直接使用接收到的表。 (完)

  • 结果相当接近,但 TVP 方法最终平均表现更好,即使数据略超过 1000 行。

    请注意,此导入过程会连续运行数千次,因此只需计算完成所有合并所需的小时数(是的,小时数)即可轻松获得平均时间。

    最初,平均合并几乎需要 8 秒才能完成(在正常负载下)。移除 NetBIOS kludge 并切换到 SqlBulkCopy 将时间缩短到几乎 7 秒。切换到 TVP 进一步将时间减少到 5.2 秒 每批。对于运行时间以小时为单位的进程来说,吞吐量提高了 35% - 所以一点也不差。与 SqlBulkCopy 相比,它也有大约 25% 的改进。

    我实际上相当有信心,真正的改进远不止于此。在测试过程中,很明显最终合并不再是关键路径;相反,正在执行所有数据处理的 Web 服务开始因传入的请求数量而崩溃。CPU 和数据库 I/O 都没有真正达到极限,并且没有明显的锁定事件。在某些情况下,我们看到连续合并之间有几秒钟的空闲时间间隔。使用 SqlBulkCopy 时有轻微的差距,但要小得多(半秒左右)。但我想这将成为另一天的故事。

    结论:对于在中型数据集上运行的复杂导入+转换过程, 表值参数确实比 BULK INSERT 操作执行得更好。

    我想补充一点,只是为了减轻部分支持临时表的人的担忧。在某种程度上,整个服务是一个巨大的登台过程。该过程的每一步都经过严格审核,因此我们不需要临时表来确定某些特定合并失败的原因(尽管实际上它几乎从未发生过)。我们所要做的就是在服务中设置一个调试标志,它会中断调试器或将其数据转储到文件而不是数据库中。

    换句话说,我们已经对流程有足够的了解,不需要临时表的安全性;我们首先拥有临时表的唯一原因是为了避免在其他情况下必须使用的所有 INSERTUPDATE 语句的颠簸。在最初的过程中,暂存数据无论如何只在暂存表中存在几分之一秒,因此它在维护/可维护性方面没有增加任何值(value)。

    还要注意,我们有 而不是 用 TVP 替换了每一个 BULK INSERT 操作。处理大量数据和/或除了将数据扔到数据库之外不需要对数据做任何特殊处理的几个操作仍然使用 SqlBulkCopy 我并不是说 TVP 是一种性能 Elixir ,只是在这个特定实例中,它们在 SqlBulkCopy 上取得了成功,涉及初始分段和最终合并之间的多个转换。

    所以你有它。指向 TToni 寻找最相关的链接,但我也很欣赏其他回复。再次感谢!

    最佳答案

    我还没有真正使用 TVP 的经验,但是在 MSDN here 中有一个很好的性能比较图表与 BULK INSERT。

    他们说 BULK INSERT 的启动成本较高,但此后速度更快。在远程客户端场景中,他们在大约 1000 行(对于“简单”服务器逻辑)处绘制线。从他们的描述来看,我会说你应该可以使用 TVP。性能下降 - 如果有的话 - 可能可以忽略不计,并且架构优势似乎非常好。

    编辑:附带说明,您可以通过使用 SqlBulkCopy 对象避免服务器本地文件并仍然使用批量复制。只需填充一个 DataTable,并将其提供给 SqlBulkCopy 实例的“WriteToServer”-Method。易于使用,而且速度非常快。

    关于bcp/BULK INSERT 与表值参数的性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2149897/

    相关文章:

    c++ - 使用 ADO 命令对象实现大容量/批量插入

    c# - 批量插入Sql Server百万条记录

    java - 应用程序关闭时发出通知

    c# - LINQ Count() until,这样效率更高吗?

    javascript - 在这个简单的神经网络示例中,为什么 Tensorflow 比 convnetjs 慢 100 倍?

    sql-server - 将日期时间变量更改为仅变量的年份

    SQL Server 如何检索两个数字之间的所有数字

    sql - 使用sql server添加特定日期、年、月、周

    java - 超 SQL (HSQLDB) : massive insert performance

    iphone - Objective C iPhone 性能问题