假设我的更新查询如下所示:
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
谢谢!
发布于 2013-07-19 07:09:02
阻塞与死锁
我认为您可能将锁定和阻塞与死锁混淆了。
在任何更新查询中,SQL server都会锁定所涉及的数据。当此锁处于活动状态时,其他进程将被阻止(延迟)编辑数据。如果最初的更新花费了很长时间(从用户的角度来看,比如几秒钟),那么前端系统可能会“挂起”,甚至使用户前端进程超时,并报告错误。
这不是死锁。这种阻塞将自行解决,基本上是非破坏性的,通过稍微延迟用户,或者在某些情况下,通过强制前端对超时进行智能处理。在问题是由于长时间运行的更新而阻塞时,您可以通过增加前端超时来修复用户必须重新提交的问题。
但是,无论您增加多少超时,死锁都无法解决。一个或多个进程将因偏见而终止(丢失更新)。
死锁与阻塞有不同的根本原因。死锁通常是由前端不一致的顺序逻辑引起的,该逻辑访问和锁定来自前端两个不同部分中不同顺序的两个表的数据。当这两个部分在多用户环境中并发操作时,它们可能基本上不确定地导致死锁和基本上无法解决的数据丢失(直到死锁的原因得到解决),而不是通常可以处理的阻塞。
管理阻塞
SQL server会选择行锁还是整个表锁?
一般来说,它取决于并且每次都可能是不同的。根据查询优化器确定将受影响的行数,锁可以是行或表。如果它超过了某个阈值,它就会进入表中,因为它会更快。
如何在遵守事务完整性的基本原则的同时减少阻塞?
SQL server将尝试锁定要联接到的表,因为这些表的内容对于生成更新的结果集非常重要。您应该能够显示更新的估计执行计划,以查看将根据今天的表大小锁定哪些内容。如果预测的锁是table,您可以使用row lock提示覆盖,但这不能保证没有阻塞。它可以减少无意中阻塞表中可能不相关的数据的机会。从本质上讲,你将总是得到数据的阻塞直接更新的材料。
不过,请记住,;
还要记住,连接表上的锁将是共享锁。这意味着其他进程仍然可以从这些表中读取,只是它们不能更新它们,直到您使用它们作为参考完成更新。相反,如果您更新的数据具有排它锁(正在更新的主表),则其他进程将在尝试简单地读取数据时主动阻塞。
因此,联接的表仍然可以读取。要更新的数据将以独占方式锁定为一组记录,直到更新完成或失败并作为一组回滚。
发布于 2013-07-19 05:30:06
我会把索引放在你的外键上,它可以加速更新和删除操作,并缓解你的死锁情况。
发布于 2018-07-20 04:23:36
我在尝试更新一个具有800K记录的表时遇到了完全相同的问题,该表连接到另一个具有10个连接条件的表。这一更新耗时超过30分钟。
通过创建一个只包含需要更新的行的临时表,我将时间缩短到了8秒。然后我用这些结果更新了第一个表,实际只需要更新20,000行。默认情况下,select命令不会被记录,我相信(但我不确定),当您创建一个带有SELECT INTO的临时表时,它也不会被记录下来(有人请确认一下)。
在与另一个大表联接的大表上发出更新时,您会将每个更新的字段记录到事务日志中,然后搜索下一个可用的候选字段,然后再次记录更改,并进行搜索。如果您可以接受脏读,那么创建一个只包含要更新的记录的临时表,将极大地减少更新时间,从而减少发生死锁的可能性。
删除where中的任何函数也很重要,例如ISNULL或甚至计算的字符串比较。这些会从根本上增加你的更新时间。
https://stackoverflow.com/questions/17723452
复制相似问题