2010-06-02 69 views
14

比方说,您有一个包含大量文本数据的大约500万条记录和nvarchar(max)列的表格。如果SomeOtherColumn = 1以最快的方式设置,您希望将此列设置为NULL大规模更新的最快方法

蛮力UPDATE在这里工作不好,因为它会创建大型隐式事务,并且永远持续下去。

在小批量的50K记录中一次更新可行,但在强壮的32核心/ 64GB服务器上仍需要47小时才能完成更新。

有什么办法可以更快地完成此更新?是否有任何神奇的查询提示/表格选项牺牲了别的东西(比如并发)来换取速度?

注意:创建临时表或临时列不是一个选项,因为此nvarchar(max)列涉及大量数据,因此会消耗大量空间!

PS:是的,SomeOtherColumn已经编入索引。

+0

另请参阅:http://stackoverflow.com/questions/571750/make-sql-server-faster-at-manipulating-data-turn-off-transaction-logging – 2010-06-02 03:22:28

+0

你是如何做'50K批记录在一次更新?它是否与存储过程?如果是这样,你可以把代码? – Fede 2010-06-02 03:30:10

+0

@ user356004:在重新阅读时,我不禁想到服务器负载很重或者设置不正确:那些时间看起来非常高。 – 2010-06-02 04:07:59

回答

1

您是否尝试过在someOtherColumn上放置索引或统计信息?

+0

如果性能问题是因为没有索引,并且需要进行表扫描来识别要更新的行,那么创建新索引并发布更新需要的时间长(或更长)? – 2010-06-02 03:18:57

+0

真的是nvarchar(max)列的索引吗? – Paparazzi 2017-01-08 19:41:30

3

您可以将数据库恢复模式设置为Simple以减少日志记录,但是如果不考虑生产环境的全部影响,则不这样做。

表上有什么索引?鉴于批量更新约。 50,000行需要这么长时间,我会说你需要一个索引。

0

尝试索引'SomeOtherColumn'... 50K记录应该快速更新。如果已经有索引,看看索引是否需要重新组织,并且已经收集了统计数据。

0

如果您正在运行的生产环境没有足够的空间来复制所有表格,我相信您迟早会在寻找麻烦。

如果你提供了有关SomeOtherColumn = 1的行数的一些信息,或许我们可以想到另一种方式,但我建议:

0)备份你的表 1)指数的标志栏 2)套装将表选项设置为“无日志传输”...如果可能的话 3)编写存储过程以运行更新

+0

顺便说一句...你需要在生活中多次运行这个程序吗? – 2010-06-02 03:24:04

+1

如何将表选项设置为“无日志传输”? – user356004 2010-06-07 09:56:36

3

希望您已经将您设置为空的列上的所有索引都删除,包括全文索引。如前所述,暂时关闭事务和日志文件就可以解决问题。备份你的数据通常也会截断你的日志文件。

+0

绝对要确保你放弃索引。过去我已经为我缩短了很多事情。 – 2010-06-02 04:09:25

1

这真的帮了我。我用这个从2小时到20分钟。

/* I'm using database recovery mode to Simple */ 
/* Update table statistics */ 

set transaction isolation level read uncommitted  

/* Your 50k update, just to have a measures of the time it will take */ 

set transaction isolation level READ COMMITTED 

根据我的经验,在2005年MSSQL工作,移动每天(自动)从一台400万46字节记录(不为nvarchar(最大)虽然)数据库中的其他表在不同的数据库中提取在QuadCore 8GB,2Ghz服务器中约20分钟,并且不会影响应用程序性能。通过移动我的意思是插入选择,然后删除。即使删除的表有28M记录,并且它每分钟不停地产生4K插入但没有更新,CPU使用率也不会超过30%。那么,这是我的情况,它可能会有所不同,这取决于您的服务器负载。

未提交读

“指定的语句(您的更新),可以读取已被其他事务修改但尚未提交的行。”就我而言,这些记录是只读的。

我不知道什么rg-tsql的意思,但here你会发现有关MSSQL中事务隔离级别的信息。

+1

“rg”是RedGate,一家赞助公司,他们在[tsql]标签的结果上做广告。 – Corey 2010-06-02 04:09:43

+1

请务必小心,并确保您了解阅读未登记交易的含义。是的,您的流程在删除条目之前不必等待打开的事务提交,但当然如果事务没有提交,所有这些都意味着您错误地删除了该行! – Cobusve 2010-06-07 10:43:45

7

从我可以看到的一切看起来并不像你的问题与索引有关。

关键似乎在于你的nvarchar(max)字段包含“大量”数据。考虑SQL执行此更新所需执行的操作。

由于您正在更新的列可能超过8000个字符,因此将其存储在页外,这意味着当该列不为NULL时,需要额外努力读取此列。

当您运行一批50000更新时,SQL必须将其置于隐式事务中,以便在出现任何问题时能够回滚。为了回滚它必须将该列的原始值存储在事务日志中。

假设(为了简单起见)每列平均包含10,000个字节的数据,这意味着50,000行将包含大约500MB的数据,这些数据必须临时存储(以简单恢复模式)或永久存储(完全恢复模式)。

无法禁用日志,因为它会危及数据库的完整性。

我在我的狗慢桌面上运行了一个快速测试,运行的批量甚至10,000变得非常慢,但将大小降低到1000行,这意味着临时日志大小约为10MB,工作得很好。

我加载了一个包含350,000行的表格,并标记了50,000个用于更新的表格。这项工作大约在4分钟内完成,而且由于它线性扩展,因此我应该可以在我的1处理器2GB桌面上大约6小时内在我的狗慢桌面上更新整个5百万行,因此我期望在您的强大服务器上支持更好通过SAN或其他东西。

您可能希望将更新语句作为select运行,只选择主键和大型nvarchar列,并确保它按预期运行。

当然,其他用户可能会锁定其他用户锁定服务器上的存储或内存中的内容或争用,但由于您没有提及其他用户,因此我将假设您拥有单用户模式下的DB。

作为一种优化,您应确保事务日志位于与数据不同的物理磁盘/磁盘组上,以最大限度地缩短寻道时间。

相关问题