SQL Server中nolock的实际用途是什么?

4

我在MSDN上阅读一些文档,其中提到“不会发出共享锁来防止其他事务修改当前事务读取的数据”。

换言之,这会导致脏读问题。这太危险了,那么为什么还要使用它呢?

有人知道实际应用场景吗?


1
总之,说读取潜在的脏信息“太危险”是一种极端的过度简化。在许多情况下,这是完全可以接受的。 - Andrew Barber
安德鲁,我不同意。有时候你需要读取脏数据,并且不能等待锁定。在这种情况下,这不仅不危险,而且是正确的行为。 - David Manheim
正如安德鲁所提到的,脏读通常是可以接受的,并且查询性能的提高是一个好处。然而,如果您在多个表上进行长时间事务处理,其中包含多个插入操作,有时需要回滚,则可能不适合使用脏读。如果您保持事务较小,则可以放心使用NOLOCK - 或设置隔离级别。 - SQLMason
我认为这就像HTML的iframe一样。我们被“教导”要尽可能避免使用它们,但在正确的情境下,它们是完成工作的正确工具。 - SQLMason
1
@DavidManheim 是的,这就是我的意思。OP似乎在说脏读太危险了。我建议这并不是事实;很多时候,这是完全可以接受的(正如你所指出的那样),因为你想要数据快速而不一定是100%准确 - Andrew Barber
8个回答

5

在我们之前的工作中,我们使用这个方法来获取大致数字。例如,如果有一张表格记录了一天内发送的数百万封电子邮件,如果在下午5点时想要查看“我们现在的情况”,我们可以说:

SELECT COUNT(*) FROM dbo.MessageTable WITH (NOLOCK)
  WHERE CampaignID = x AND CustomerID = y;

很多人会建议在没有 WHERE 子句的情况下使用这个语句来统计 COUNT(*)。但我认为,如果你愿意接受一些不准确性,也可以这样做:COUNT(*)
SELECT SUM(rows) FROM sys.partitions
  WHERE [object_id] = OBJECT_ID('dbo.tablename')
  AND index_id IN (0,1);

由于在飞行中的交易,此数字同样不准确,但实际上并不需要扫描表格,因此效率要高得多。对于我们的情况,即使对于子集,我们也可以使用它:在筛选索引就位(用于其他目的)后,我们可以类似地查询sys.partitions,但使用筛选索引的index_id

然而,在大多数情况下,使用NOLOCK可能感觉像是一个加速按钮,但它可能造成的不准确性很少值得这样做。除非您的系统已经严重受到tempdb限制,否则应考虑当前场景下使用READ_COMMITTED_SNAPSHOT而不是NOLOCK。请参见Read Committed Snapshot的优缺点


1
通常我会使用这个查询一个相对繁忙的表,该表通常用于记录日志。 SELECT TOP 10 * FROM dbo.MessageLog (NOLOCK) WHERE AppCode = 'DesktopApp' ORDER BY MessageDate DESC 该表的记录主要是写入一次,从不更新。

1
是的。我以前也用过它来处理这种情况,当你知道你的读取查询可能会被并发插入阻塞,并且也知道回滚插入要么很不可能发生,要么即使你最终读取到已回滚的数据也没有关系。READPAST提示可以用于仅读取已提交的数据。 - Martin Smith

1
并非所有数据都是平等的。相当多的数据实际上并不是关键的,因此在某些情况下,进行脏读取是可以接受的。例如,批处理过程可能会批量更新多个不同的表。您可能想要读取用户的已读消息数量。您更希望数字比实际值高1或2,而不是让用户等待几秒钟、几分钟或几小时以获取锁。

换句话说,当实际上并不需要精确数据时,您正在增加并发性,并且有一些(潜在的)无效数据是可以接受的。


1

我们有一个表格,保存着一个队列,只会插入和更新内容;从未删除过任何内容。这些行有不同的标志,表示相关进程正在发生什么。生产系统仅使用行锁,但同时在各个行上持有数十个锁,以便不同的进程可以同时进行。

我们检查仍在处理的项目数量,以避免过载系统。当用户启动新进程时,它等待生成子进程,直到队列中当前正在处理的项目少于10个为止。

我们需要防止在可能需要锁定其自身状态以更新其状态的进程上占用锁,并且需要查看已锁定项目的状态。我们使用 with (nolock) 来避免等待查看队列中正在发生的情况,并计算尚未标记为完成的项目数量 - 这由逻辑保证,仅在进程完成时才会发生这种情况。


1

根据情况,如果您愿意接受一些记录可能已过期的事实,它可以提供更快的访问。

例如:

SELECT COUNT(*) FROM mytable (nolock) 

在大型表格上,将使用更少的资源并且通常会更快。

SELECT COUNT(*) FROM mytable 

2
在这种情况下,最好查询sys.partitions,它的准确度与SELECT COUNT(*) WITH (NOLOCK)相当,但速度更快。 - Aaron Bertrand

0

你显然是个天才。但它不应该被使用。

NOLOCK经常被滥用作为加速数据库读取的神奇方法,但我尽可能避免使用它。

结果集可能包含尚未提交的行,这些行通常稍后会被回滚。

错误或结果集可能为空,缺少行或多次显示相同的行。

这是因为其他事务正在同时移动数据,而您正在读取它。

READ COMMITTED还会增加一个问题,即在多个用户同时更改同一单元格的情况下,数据会损坏。

还有其他副作用,这导致了您最初希望获得的速度提升的牺牲。

现在你知道了,永远不要使用它。


0

我们在具有大量读取但很少写入的表上使用它。如果连接只是读取数据,则通常进行脏读取不会有危险。这可以防止在表上阻塞,从而提高性能。

请查看https://dev59.com/6HM_5IYBdhLWcg3wQQzw#1453000以获取有关nolock及其好坏的更多信息。


1
如果没有读取脏数据的危险,那么为什么要使用提示?只有在存在脏数据时,它才会实际上有用。在您描述的情况下,SQL Server通常也不会获取S锁。请参阅缺失共享锁案例 - Martin Smith
抱歉,可能表述不够清晰。由于写入操作较少,存在脏读的风险,但是执行读取操作的连接只是读取数据,我们并不关心是否会出现脏读(例如日志表,甚至像员工姓名这样很少更改的内容)。 - Greg

0
相对而言很容易 - 我们使用nolock提示获取非财务(通常很少更改)的数据(例如客户地址、商品描述或各种配置选项),使用“正常”的锁定获取财务或数量数据(例如价格或余额)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接