sql server-使用join更新时表何时被锁定

时间:2013-07-18 12:28:53

标签: sql sql-server

假设我的更新查询如下所示:

UPDATE a SET 
    a.colSomething= 1
FROM tableA a WITH (NOLOCK)
INNER JOIN tableB b WITH (NOLOCK) 
        ON a.colA= b.colB
INNER JOIN tableC c WITH (NOLOCK) 
        ON c.colC= a.colA

假设以上连接到tableB和tableC需要几分钟才能完成。在表/行锁定方面,整个表在连接期间是否被锁定?或者sql编译器是否足够智能以避免锁定整个表?

与上面的查询相比,通过在实际更新之前首先将连接结果存储在临时表中,不太可能出现死锁,如下所示?

SELECT a, b, c
INTO    
FROM tableA 
INNER JOIN tableB b WITH (NOLOCK) 
    ON a.colA= b.colB
INNER JOIN tableC c WITH (NOLOCK) 
    ON c.colC= a.colA

UPDATE a SET a.colSomething=1 
FROM tableA a INNER JOIN #tmp t ON a.colA= t.colA

谢谢!

3 个答案:

答案 0 :(得分:5)

阻止与死锁

我认为你可能会因为DEADLOCKS而使用锁定和阻止而感到困惑。

在任何更新查询中,SQL Server将锁定所涉及的数据。当此锁定处于活动状态时,其他进程将被阻止(延迟)编辑数据。如果原始更新需要很长时间(从用户的角度来看,比如几秒钟),那么前端系统可能会“挂起”甚至超时用户前端进程并报告错误。

这不是僵局。这种阻塞将自行解决,基本上非破坏性地通过稍微延迟用户或在某些情况下通过强制前端对超时是聪明的。由于长时间运行更新,问题是阻塞,您可以通过增加前端超时来修复必须重新提交的用户。

无论你增加多少超时,都无法解决死锁问题。一个或多个流程将以偏见终止(失去更新)。

死锁具有与阻止不同的根本原因。死锁通常是由前端不一致的顺序逻辑引起的,它在前端的两个不同部分以不同的顺序访问和锁定来自两个表的数据。当这两个部分在多用户环境中同时运行时,它们可能基本上,非确定性地导致死锁,并且基本上无法解决数据丢失(直到解决死锁的原因),而不是通常可以处理的阻塞。

管理屏蔽

SQL服务器会选择行锁还是整个表锁?

通常,它每次都取决于并且可能不同。根据查询优化器确定将影响的行数,锁可以是行或表。如果它超过一定的阈值,它将进入表格,因为它会更快。

如何在遵守事务完整性的基本原则的同时减少阻塞?

SQL Server将尝试锁定您要加入的表,因为它们的内容对于生成更新的结果集很重要。您应该能够显示更新的估计执行计划,以查看将根据当前表的大小锁定的内容。如果预测的锁是表,则可以使用行锁提示覆盖,但这并不能保证不会阻塞。它可以减少无意中阻止表中可能不相关的数据的机会。您基本上总是会直接阻止数据直接更新。

请记住,

另请注意,连接表上的锁定将是共享锁。这意味着其他进程仍然可以从这些表中读取,它们无法更新它们,直到您使用它们作为参考完成更新。相反,其他进程将主动阻止尝试简单地读取您更新的数据具有独占锁定(主表正在更新)。

因此,仍然可以读取已连接的表格。要更新的数据将被独占锁定为一组记录,直到更新完成或失败并作为一个组回滚。

答案 1 :(得分:0)

我会在您的外键上放置索引,它可以加快更新和删除操作,并缓解您的死锁情况。

答案 2 :(得分:0)

我遇到了完全相同的问题,试图更新具有800K条记录的表并连接到具有10个连接条件的另一个表。此更新最多需要30分钟。

我通过创建一个仅包含需要更新的行的临时表将其减少到8秒。然后,我用这些结果更新了第一个表,只需要更新20,000个实际行。 select命令默认不会被记录,并且我相信(但不确定),当您使用SELECT INTO创建临时表时,它也不会被记录(请确认)。

在与另一个大表连接的大表上发布更新时,您会将每个更新的字段记录到事务日志中,然后搜索下一个可用的候选者,然后再次记录更改并进行搜索。如果您可以接受脏读,然后仅使用将要从FROM中更新的记录创建一个临时表,则将大大减少您的更新时间,从而减少死锁的可能性。

删除您所在位置的任何函数(例如ISNULL甚至是计算得出的字符串比较)也很重要。这些可以从根本上增加您的更新时间。