在大量使用频繁的表上运行更新

时间:2012-01-04 17:30:21

标签: sql

我在SQL Server 2005中有一个大表(大约1.7亿行,2个nvarchar和7个int列),不断被插入。从性能角度来看,一切正常,但每隔一段时间我就必须更新表中导致问题的一组行。如果我更新一小组数据,它可以正常工作,但是如果我必须更新一组40,000条记录,那么它需要大约3分钟并且在表上会阻塞,因为插入开始失败会导致问题。

如果我只是运行一个选择来取回需要更新的数据,我会在大约2秒内收回40k记录。这只是永远需要的更新。这反映在更新的执行计划中,其中聚集索引更新占用了成本的90%,而索引搜索和顶级运算符使得行占用了成本的10%。我正在更新的列不是任何索引键的一部分,所以它不像重新组织任何东西。

有没有人对如何加快这个问题有任何想法?我现在的想法是编写一个服务,只看到这些更新何时发生,撤回必须更新的记录,然后循环并逐个更新它们。这将满足我的业务需求,但它是另一个维护的模块,如果我能从DBA方面解决这个问题,我很乐意。

感谢您的任何想法!

4 个答案:

答案 0 :(得分:1)

实际上,如果更新nvarchar列,它可能会重新组织页面。 根据更新对这些列的作用,它们可能会导致记录比更新前为其保留的空间大。 (现在请参阅解释nvarchar存储在http://www.databasejournal.com/features/mssql/physical-database-design-consideration.html。)

所以说一条记录在nvarchar中保存了20个字符的字符串 - 这需要空间中20 * 2 + 2(指针为2)字节。这是在初始插入表中写入的(基于索引结构)。 SQL Server将只使用nvarchar所需的空间。

现在更新并插入一个包含40个字符的字符串。而oops,索引的叶子结构中的记录空间突然太小。所以关闭将记录转移到另一个物理位置,旧指针中的指针指向更新记录的实际位置。

这会导致你的索引过时,因为整个物理结构需要改变,你会看到很多索引工作在幕后进行。很可能导致独占表锁升级。

不确定如何最好地处理此问题。个人如果可能的话我采取独占表锁,删除索引,进行更新,重新索引。因为您的更新有时会导致索引过时,这可能是最快的选择。但是这需要一个维护窗口。

答案 1 :(得分:1)

您应该将更新批量更新为多个更新(例如10000次,TEST!),而不是一个40k行的更新。

这样你就可以避免表锁,SQL Server在esclating到表锁之前只会取出5000个锁(页面或行),甚至这个也不是很容易预测(内存压力等)。在这种情况下进行的较小更新至少可以避免您遇到的并发问题。

您可以使用服务或firehose游标批量更新。

阅读本文以获取更多信息: http://msdn.microsoft.com/en-us/library/ms184286.aspx

希望这有帮助

罗伯特

答案 2 :(得分:0)

正如你所提到的,最强大(最简单)的方法是提供基本服务。这样做的好处是能够随服务器上的负载和/或数据负载进行扩展。

例如,如果您有一组必须发生 ASAP 的更新,那么您可以调高批量大小。相反,对于不太重要的更新,如果每次更新都花费“太长时间”以减轻数据库的某些压力,则可能会使更新“服务器”变慢。

这种“心跳”过程在系统中相当常见,并且在正确的情况下可以非常强大。

答案 3 :(得分:0)

你的分析师说它需要时间来更新聚簇索引。更新时数据的大小是否发生了变化?似乎varchar正在驱动要重新组织的数据,这可能需要更新索引指针(正如已经指出的KMB)。在这种情况下,您可能希望增加数据和索引页面上的%可用大小,以便数据和索引页面可以增长而无需重新链接/重新分配。由于更新是IO密集型操作(与读取不同,可以缓冲),性能也取决于几个因素

1)您的表是否按数据分区2)整个表是否位于同一个SAN磁盘中(或者SAN条件是否良好?)3)事务日志记录的详细程度。可以增加事务日志的缓冲区大小以支持更大的日志写入以支持大量插入吗?

您使用哪种API /语言也很重要?例如,JDBC支持批量更新功能,如果您进行多次更新,它会使更新有效。