了解SELECT查询上的SQL Server LOCKS


78

我想知道SELECT WITH (NOLOCK)如果影响该表的其他唯一查询是SELECT查询,在表上使用的好处是什么?

SQL Server如何处理?一个SELECT查询会阻止另一个SELECT查询吗?

我正在使用SQL Server 2012和Linq-to-SQL DataContext

(编辑)

关于性能:

  • 如果使用锁定功能,第二个SELECT必须等待第一个SELECT完成SELECT吗?
  • 与一个SELECT WITH (NOLOCK)

Answers:


177

SELECTSQL Server中的A将在表行上放置一个共享锁-第二个SELECT也将需要共享锁,并且它们彼此兼容。

所以没有人SELECT可以阻挡别人SELECT

什么WITH (NOLOCK)查询提示用于是能够读取数据的插入(由其它连接)的过程,是和尚未提交。

如果没有该查询提示,则SELECT可能会通过正在执行INSERT(或UPDATE)的语句在行(或可能是整个表)上设置排锁的语句来阻止a读取表,直到该操作的事务已提交(或回滚)为止。

WITH (NOLOCK)提示的问题是:您可能正在读取最终根本不会插入的数据行(如果INSERT事务回滚了),因此您的例如报告可能显示从未真正提交到数据库的数据。

还有另一个查询提示可能有用- WITH (READPAST)。这指示SELECT命令仅跳过它尝试读取且被独占锁定的任何行。该SELECT不会阻止,它将无法读取任何“脏”未提交的数据-但它可能会跳过某些行,如不显示在表中所有的行。


1
好的答案,非常感谢!没有理由SELECT使用会对数百个查询产生影响WITH (NOLOCK)吗?
弗朗西斯·P

3
我们无选择地使用了我们选择的99.5%,没有开玩笑。如果管理员正在更新用户记录,则您不希望该记录使报告坐在那里并等待整个分布式事务完成。因此,他们的旧数据显示在报告中。谁在乎?如果该报告在第二秒之前运行,则该数据与使用行锁保存的数据相同。唯一需要关注的地方是尚未提交的数据。如果您显示的是“最近一小时的订单”,则可能是一个问题,但与速度/并发收益相比,这是一个很小的问题。
布莱恩·怀特

5
另外,由于以“报告”为例,因此报告通常是在过去5分钟之内的时间段。nolock报告上个月的数据-并不是一个月后数据会回滚。
布莱恩·怀特

2
@FrancisP:如果您插入少量行,则不会-在这种情况下,它只会锁定要插入的新行。如果一次插入大约5000行以上-则会发生锁升级,并且整个表将被独占锁定。
marc_s

1
很好的答案。。感觉就像是SQL锁的多合一教程!!很高兴我进了这里!
digitally_inspired

32

在性能方面,您会一直专注于选择。
共享不会阻止读取。
共享锁块更新。
如果您有数百个共享锁,则它需要花费一段时间才能获得排他锁,因为它必须等待共享锁清除。

默认情况下,选择(读取)具有共享锁。
共享(S)锁允许并发事务读取(SELECT)资源。
共享锁不会影响其他选择(1或1000)。

区别在于nolock和共享锁效果如何更新或插入操作。

资源上存在共享(S)锁时,没有其他事务可以修改数据。

共享锁会阻止更新!
但是nolock不会阻止更新。

这可能对更新的性能产生巨大影响。它还会影响刀片。

脏读(nolock)听起来很脏。您永远不会获得部分数据。如果更新将John更改为Sally,那么您永远都不会得到Jolly。

我经常使用共享锁来实现并发。数据一被读取就过时。在下一个毫秒内将John更改为Sally的读数是陈旧的数据。对Sally的读取会在下一个毫秒内回滚到John,这是陈旧的数据。那是毫秒级的。我有一个数据加载器,如果用户使用共享锁,则需要20个小时才能运行;如果用户没有锁,则需要4个小时才能运行。在这种情况下,共享锁导致数据过时16小时。

不要误用nolock。但是他们确实有地方。如果要在字节设置为1时削减支票,然后在削减支票时将其设置为2-则不是空锁的时间。


2
谢谢。我们看到类似的性能特征。如果我们需要读取锁,则我们的网站将无法运行,并且在大多数情况下没有它的影响将是微不足道的。
布莱恩·怀特

@BrianWhite谢谢。有人知道。而且我在更新和插入上使用了很多表锁。进入,完成和离开是我的方法。
狗仔队2012年

2
脏读(nolock)听起来很脏。您永远不会获得部分数据。如果更新将John更改为Sally,那么您永远都不会得到Jolly。-我们读约翰对吗?
MonsterMMORPG's

2
sql服务器中的更新使用更新锁(U),该锁随后会转换为互斥锁(X)。(请参阅madeiradata.com/role-update-lock-sql-server)更新锁不会阻止共享锁,但排他锁会阻止所有其他锁(请参阅msdn.microsoft.com/zh-cn/library/ms186396(v= sql.105).aspx)。
kolobok

@kolobok将需要一段时间才能获得排他锁
狗仔队

10

我必须添加一个重要的评论。每个人都提到NOLOCK只读取脏数据。这不准确。也有可能两次读取相同的行,或者在读取过程中跳过了整行。原因是当SQL Server重新平衡b树时,您可能同时要求一些数据。

检查其他线程

https://stackoverflow.com/a/5469238/2108874

http://www.sqlmag.com/article/sql-server/quaere-verum-clustered-index-scans-part-iii.aspx

使用NOLOCK提示(或将会话的隔离级别设置为READ UNCOMMITTED),您告诉SQL Server您不希望保持一致性,因此无法保证。请记住,尽管“不一致的数据”不仅意味着您可能会看到未提交的更改(后来回滚),或者数据处于事务的中间状态。这也意味着在扫描所有表/索引数据的简单查询中,SQL Server可能会丢失扫描位置,或者您可能最终两次获得同一行。


9

在我的工作中,我们有一个非常大的系统,可以同时在多台PC上运行,并且有非常大的表,其中包含数十万行,有时甚至是几百万行。

当您在一个非常大的表上进行SELECT时,假设您想知道用户在过去10年中进行的每笔交易,并且该表的主键不是以有效的方式构建的,因此查询可能需要几分钟的时间跑步。

然后,我们的应用程序可能会同时在许多用户的PC上运行,并访问相同的数据库。因此,如果有人试图将另一个SELECT正在读取的表插入到表中(在SQL试图读取的页面中),则可能发生LOCK,并且两个事务相互阻塞。

我们必须在SELECT语句中添加“ NO LOCK”,因为这是对表的巨大SELECT,同时很多用户都在使用该表,并且一直都有LOCKS。

我不知道我的例子是否足够清楚?这是一个真实的例子。


谢谢您的示例,但我只是想知道SELECT查询是否会影响其他SELECT查询(在同一张桌子上)。–
Francis P

1
它们不会,但是select语句可能是包含更新的事务的一部分。更新tbl集x =(从tbl选择max(y)),其中z =(从tbl选择min(a))。如果您从tbl中有一个并发选择z,其他选择不会阻止它,但更新会被阻止。
布莱恩·怀特

1
我确实遇到了这个问题,长时间的选择阻碍了我的插入
nojetlag

2
事务不会互相阻止-选择将阻止更新。这里有几个有趣的链接,它们帮助我更加了解了这些东西的工作原理:第一
JonnyRaa 2015年

@JonnyLeeds:您的第二个链接不再起作用。这是SQL Server的
混合式(stomy)'18

3

SELECT WITH (NOLOCK)未提交的数据,这相当于具有允许读取READ UNCOMMITTED您的数据库隔离级别设置。NOLOCK与在整个数据库上设置隔离级别相比,该关键字允许更精细的控制。

Wikipedia有一篇有用的文章:Wikipedia:隔离(数据库系统)

其他stackoverflow文章中也对此进行了详细讨论。


感谢rghome提供的其他信息。
弗朗西斯·P

这就是为什么这样的用例有效,我更喜欢使用READUNCOMMITTED(的别名NOLOCK)提示。这样做使得实际的操作(实际上并不是“没有锁”)变得不太清楚。
user2864740

1

不带锁的选择-将选择可能会/可能不会插入的记录。您将读取脏数据。

例如-假设一个事务插入1000行然后失败。

选择时-您将获得1000行。


但是,如果没有记录要插入该表怎么办,那么NO LOCK是否仍然有意义?
弗朗西斯·P

不它不是。因为read使用一个共享锁,可以通过多个会话获取该锁。无法获取脏数据。
罗伊·纳米尔

我宁愿不回答我不确定的事情。:-)
罗伊·纳米尔
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.