BULK INSERT
由于架构已更改,我将不得不使用SQL Server的命令重写一些相当旧的代码,我想到也许我应该考虑改用TVP切换到存储过程,但是我想知道会有什么效果它可能会影响性能。
一些背景信息可能有助于解释为什么我问这个问题:
数据实际上是通过Web服务输入的。该Web服务将文本文件写入数据库服务器上的共享文件夹,该文件夹随后执行
BULK INSERT
。此过程最初是在SQL Server 2000上实现的,当时实际上除了INSERT
在服务器上删除几百条语句外,别无选择,这实际上是原始过程,并且会造成性能灾难。数据被批量插入到永久登台表中,然后合并到更大的表中(此后将从登台表中删除)。
要插入的数据量是“大”的,而不是“巨大的”-通常只有几百行,在极少数情况下可能是5-10k行。因此,我的直觉是,
BULK INSERT
作为一个未记录的操作不会有太大的不同(但我当然不确定,因此是问题)。插入实际上是更大的流水线批处理过程的一部分,需要连续多次进行。因此性能是至关重要的。
我想将其替换BULK INSERT
为TVP的原因是:
通过NetBIOS编写文本文件可能已经花费了一些时间,并且从体系结构的角度来看这是非常可怕的。
我认为可以(并且应该)取消登台表。造成这种情况的主要原因是,插入的数据需要在插入的同时用于其他两次更新,而尝试从大量生产表进行更新要比使用几乎为空的登台要昂贵得多表。对于TVP,参数基本上是临时表,我可以在主插入之前/之后使用它进行任何操作。
我几乎可以消除重复检查,清理代码以及与批量插入相关的所有开销。
如果服务器一次获得一些事务,则无需担心登台表或tempdb上的锁争用(我们尝试避免这种情况,但它确实发生了)。
显然,在将任何产品投入生产之前,我将对此进行概要分析,但是我认为在我度过所有时间之前先询问一下是一个好主意,看看是否有人为此发布使用TVP的严厉警告。
所以-对于任何对SQL Server 2008足够熟悉的人尝试过或至少对此进行过调查,那么结论是什么?对于插入(例如几百到几千行)的情况,TVP是否会切芥菜?与散装刀片相比,性能有显着差异吗?
更新:现在问号减少了92%!
(又称:测试结果)
经过36阶段的部署过程之后,最终结果现已投入生产。两种解决方案都经过了广泛的测试:
- 提取共享文件夹代码并
SqlBulkCopy
直接使用该类; - 使用TVP切换到存储过程。
以便读者可以了解到底经过了什么测试,以消除对这些数据的可靠性的任何疑问,这是对该导入过程实际作用的更详细说明:
从通常约20至50个数据点的时间数据序列开始(尽管有时可能会增加几百个)。
对其进行大量疯狂的处理,这些处理基本上独立于数据库。此过程是并行的,因此(1)中大约8-10个序列被同时处理。每个并行过程会生成3个附加序列。
取所有3个序列和原始序列,并将它们合并为一批。
将现在完成的所有8-10个处理任务中的批次合并为一个大的超级批次。
使用
BULK INSERT
策略(请参阅下一步)或TVP策略(跳至步骤8)导入它。使用
SqlBulkCopy
该类将整个超级批处理转储到4个永久登台表中。运行一个存储过程,该存储过程是(a)在两个表上执行一系列聚合步骤,包括几个
JOIN
条件,然后(b)MERGE
在6个生产表上使用聚合和非聚合数据执行a 。(完成)要么
生成4个
DataTable
对象,其中包含要合并的数据;其中3个包含CLR类型,但不幸的是ADO.NET TVP没有正确地支持它们,因此必须将它们作为字符串表示形式使用,这会严重影响性能。将TVP馈送到存储过程,该过程基本上与(7)相同,但直接与接收到的表一起进行。(完成)
结果相当接近,但是TVP方法最终平均表现更好,即使数据超过1000行少量。
请注意,此导入过程连续运行了数千次,因此仅通过计算完成所有合并所花费的小时数(是,小时),就很容易获得平均时间。
最初,平均合并几乎要花8秒钟才能完成(在正常负载下)。消除NetBIOS的麻烦并切换为SqlBulkCopy
将时间减少到几乎恰好7秒。切换到TVP可以将时间进一步减少到每批5.2秒。对于运行时间以小时为单位的过程,这将使吞吐量提高35% -一点也不差。与相比,也提高了约25%SqlBulkCopy
。
实际上,我相当有信心,真正的进步远不止于此。在测试过程中,很明显,最终的合并不再是关键的路径。相反,进行所有数据处理的Web服务开始受到输入请求数量的限制。CPU和数据库I / O都没有真正达到极限,并且没有明显的锁定活动。在某些情况下,我们发现连续合并之间存在几秒钟的空闲间隔。使用时有一点缝隙,但要小得多(半秒左右)SqlBulkCopy
。但是我想那将成为另一天的故事。
结论:表值参数确实比BULK INSERT
在中型数据集上执行复杂的导入+转换过程的操作要好。
我想补充一点,只是为了缓解对部分赞成登台的人的担忧。从某种意义上说,整个服务是一个巨大的升级过程。该过程的每一步都经过了严格的审核,因此我们不需要临时表来确定某些特定合并失败的原因(尽管在实践中几乎从未发生过)。我们要做的就是在服务中设置一个调试标志,它将中断调试器或将其数据转储到文件而不是数据库中。
换句话说,我们已经对流程有足够的洞察力,不需要临时表的安全性;我们首先拥有登台表的唯一原因是为了避免对所有本来必须使用的INSERT
andUPDATE
语句造成混乱。在原始过程中,临时数据无论如何仅停留在临时表中几分之一秒,因此它在维护/可维护性方面没有任何价值。
还要注意,我们还没BULK INSERT
有用TVP代替每一个操作。一些处理大量数据和/或不需要对数据做任何特殊处理的操作(除了将其扔给数据库外)仍在使用SqlBulkCopy
。 我并不是说TVP是性能的灵丹妙药,只是他们SqlBulkCopy
在此特定情况下成功了,包括在初始阶段和最终合并之间进行了多次转换。
所以你有它。Point会去TToni寻找最相关的链接,但是我也很感谢其他回复。再次感谢!