Aar*_*ght 77 performance bulkinsert sql-server-2008 table-valued-parameters
我将不得不使用SQL Server的BULK INSERT命令重写一些相当旧的代码,因为架构已经改变,而且我想到也许我应该考虑用TVP切换到存储过程,但我想知道是什么影响它可能有性能.
一些背景信息可能有助于解释我为什么问这个问题:
数据实际上是通过Web服务提供的.Web服务将文本文件写入数据库服务器上的共享文件夹,该文件夹依次执行BULK INSERT.这个过程最初是在SQL Server 2000上实现的,当时除了INSERT在服务器上丢失几百个语句之外别无选择,这实际上是原始进程并且是性能灾难.
将数据批量插入永久登台表,然后合并到更大的表中(之后将其从登台表中删除).
要插入的数据量是"大",但不是"巨大的" - 通常是几百行,在极少数情况下可能是5-10k行.因此,我的直觉是,BULK INSERT作为一个非记录操作不会产生那么大的差异(但当然我不确定,因此问题).
插入实际上是一个更大的流水线批处理过程的一部分,需要连续多次发生; 因此性能是至关重要的.
我想BULK INSERT用TVP 取代的原因是:
在NetBIOS上编写文本文件可能已经花费了一些时间,而且从架构的角度来看它非常可怕.
我相信可以(而且应该)消除临时表.它的主要原因是插入的数据需要在插入的同时用于其他几个更新,并且尝试从大量生产表进行更新比使用几乎空的分段更加昂贵表.使用TVP,参数基本上是临时表,我可以在主插入之前/之后用它做任何我想做的事情.
我几乎可以废除欺骗检查,清理代码以及与批量插入相关的所有开销.
如果服务器同时获得一些这些事务,我们无需担心登台表或tempdb上的锁争用(我们尽量避免它,但它会发生).
在将任何内容投入生产之前,我显然会对此进行分析,但我认为在我花费所有时间之前首先询问周围可能是一个好主意,看看是否有任何人有关于为此目的使用TVP的任何严厉警告.
那么 - 对于那些对SQL Server 2008足够惬意的人来说,或者至少已经对此进行了调查,那么判决是什么?对于插入,比方说,几百到几千行,经常发生,TVP切割芥末?与批量插入相比,性能是否存在显着差异?
(又名:测试结果)
最终的结果是在感觉像36阶段部署过程之后的生产中.两种解决方案都经过了广泛测试
SqlBulkCopy直接使用该类;只是让读者可以得到一个想法是什么确切地进行了测试,以消除任何怀疑这个数据的可靠性,这里是什么这个导入过程更详细的解释实际上做:
从时间数据序列开始,通常约为20-50个数据点(尽管有时可能会达到几百个);
做一大堆疯狂的处理,主要是独立于数据库.该过程是并行化的,因此(1)中的大约8-10个序列同时被处理.每个并行过程生成3个附加序列.
取所有3个序列和原始序列并将它们组合成一批.
将所有8-10个现已完成的加工任务的批次合并为一个大型超级批次.
使用BULK INSERT策略(请参阅下一步)或TVP策略(跳至步骤8)导入.
使用SqlBulkCopy该类将整个超级批处理转储到4个永久临时表中.
运行存储过程,(a)对其中两个表执行一系列聚合步骤,包括几个JOIN条件,然后(b)MERGE使用聚合和非聚合数据执行6个生产表.(成品)
要么
生成DataTable包含要合并的数据的4个对象; 其中3个包含CLR类型,遗憾的是ADO.NET TVP不能正确支持它们,因此必须将它们作为字符串表示形式推入,这会对性能造成一定影响.
将TVP馈送到存储过程,该过程基本上与(7)进行相同的处理,但是直接与接收的表一起进行.(成品)
结果相当接近,但TVP方法最终平均表现更好,即使数据少量超过1000行.
请注意,此导入过程连续运行了数千次,因此只需计算完成所有合并所需的小时数(是,小时)即可轻松获得平均时间.
最初,平均合并几乎完成了8秒(在正常负载下).删除NetBIOS kludge并切换到SqlBulkCopy将时间缩短到几乎正好7秒.切换到TVP进一步将每批次的时间减少到5.2秒.对于一个以小时计算运行时间的过程,吞吐量提高了35% - 所以一点也不差.它也提高了约25%SqlBulkCopy.
我实际上相信真正的改善远不止于此.在测试过程中,很明显最终合并不再是关键路径; 相反,正在进行所有数据处理的Web服务在进入的请求数量下开始出现问题.CPU和数据库I/O都没有真正达到最大值,并且没有重要的锁定活动.在某些情况下,我们看到连续合并之间存在几个空闲秒的间隙.使用时有一点间隙,但要小得多(半秒钟左右)SqlBulkCopy.但我想这将成为另一天的故事.
结论:表值参数确实比BULK INSERT在中型数据集上运行的复杂导入+转换过程的操作表现更好.
我想补充一点,只是为了减轻对部分主要人员的担忧.在某种程度上,整个服务是一个巨大的临时过程.流程的每一步都经过严格审核,因此我们不需要临时表来确定某些特定合并失败的原因(尽管在实践中几乎从未发生过).我们所要做的就是在服务中设置一个调试标志,它将中断调试器或将其数据转储到文件而不是数据库.
换句话说,我们已经对过程有了足够的了解,并且不需要临时表的安全性; 我们首先使用临时表的唯一原因是避免对我们本来不得不使用的所有INSERT和UPDATE语句进行颠簸.在原始进程中,登台数据无论如何都只存在于临时表中,因此它在维护/可维护性方面没有添加任何值.
另请注意,我们尚未BULK INSERT用TVP 取代每一项操作.处理大量数据和/或不需要对数据执行任何特殊操作的几个操作除了将其丢弃到数据库之外仍然使用SqlBulkCopy. 我并不是说TVP是一种性能灵丹妙药,只是他们SqlBulkCopy在这个特定的实例中成功完成了初始阶段和最终合并之间的几次转换.
所以你有它.点到TToni找到最相关的链接,但我也很欣赏其他回复.再次感谢!
我真的没有跟TVP经验,但,但有一个很好的性能对比图与在MSDN BULK INSERT 这里.
他们说BULK INSERT具有更高的启动成本,但此后更快.在远程客户端场景中,他们绘制大约1000行的行(对于"简单"服务器逻辑).从他们的描述来看,我会说你使用TVP应该没问题.性能损失 - 如果有的话 - 可能是微不足道的,而且架构效益看起来非常好.
编辑:在旁注中,您可以避免服务器本地文件,并仍然使用SqlBulkCopy对象使用批量复制.只需填充DataTable,并将其提供给SqlBulkCopy实例的"WriteToServer"-Method.易于使用,速度非常快.
关于@TToni答案中提供的链接提到的图表需要在上下文中进行.我不确定这些建议的实际研究有多少(同时请注意,该图表似乎仅在该文档的版本2008和2008 R2版本中可用).
另一方面,有来自SQL Server客户咨询团队的白皮书:使用TVP最大化吞吐量
我自2009年以来一直在使用TVP,至少根据我的经验,我发现除了简单的插入目标表而没有额外的逻辑需求之外的任何事情(这种情况很少见),那么TVP通常是更好的选择.
我倾向于避免使用临时表,因为数据验证应该在应用层进行.通过使用TVP,可以轻松容纳,并且存储过程中的TVP表变量本质上是一个本地化的临时表(因此与使用真实表进行分段时获得的同时运行的其他进程不会发生冲突).
关于在课题中进行的测试,我认为它可能比最初发现的更快:
IEnumerable<SqlDataRecord>接口更快并且使用更少的内存,因为您不在内存中复制集合仅将其发送到数据库.我在以下地方记录了这一点:
OPTION (RECOMPILE)#)并将TVP的内容复制到临时表中我想我仍然坚持使用批量插入方法。您可能会发现使用具有合理行数的 TVP 仍会命中 tempdb。这是我的直觉,我不能说我已经测试了使用 TVP 的性能(不过我也有兴趣听取其他人的意见)
您没有提到是否使用 .NET,但我为优化以前的解决方案而采取的方法是使用SqlBulkCopy类批量加载数据- 您不需要先将数据写入文件加载,只需给SqlBulkCopy类(例如)一个 DataTable - 这是将数据插入数据库的最快方法。5-10K 行并不多,我已经将它用于多达 750K 行。我怀疑一般情况下,使用 TVP 时几百行不会有很大的不同。但恕我直言,扩大规模将是有限的。
也许SQL 2008 中的新MERGE功能会让您受益?
此外,如果您现有的临时表是用于此过程的每个实例的单个表,并且您担心争用等,您是否考虑过每次创建一个新的“临时”但物理的临时表,然后在它出现时将其删除完成了吗?
请注意,您可以通过在没有任何索引的情况下填充它来优化加载到这个临时表中。然后一旦填充,在该点添加任何所需的索引(FILLFACTOR=100 以获得最佳读取性能,因为此时它不会更新)。
| 归档时间: |
|
| 查看次数: |
32047 次 |
| 最近记录: |