有人可以解释为什么 select with nolock 会查询一部分更新的数据吗?
-
16-10-2019 - |
题
我正在阅读来自的答案 这里 (来自stackoverflow,我认为应该在这里问)
NOLOCK 意味着根本不加锁。
您的查询可能会返回 UPDATE 和 截至单次查询中 UPDATE 之后的部分。
我知道 nolock 不会锁定表,因此其他人可以同时查询。
从它显示的答案和示例来看,它在数据更新时获取数据。
为什么会发生这种情况?
我假设对于正常选择,它会尝试在表上放置锁,因此当执行更新语句时,它会在行或页上放置锁。然后,当我尝试运行 select 语句时,它无法放置锁,直到更新语句锁被释放。
但在这种情况下,因为 select 语句不会尝试对表加锁,所以它可以运行而无需等待 update 语句释放锁?
解决方案
这并不完全正确 NOLOCK
意味着根本不加锁。在此提示下的查询仍将进行 Sch-S
锁和(可能 HOBT
锁)。
在下面 read committed
SQL Server 的隔离级别将(通常) 取行级别 S
锁定并在读取数据后立即释放它们。这些都是不兼容的 X
锁定未提交的更新,从而防止脏读。
在链接答案的示例中 SELECT
查询在遇到修改的行时不会被阻止,因此很可能读取部分更新。
默认情况下也可能发生 read committed
隔离级别也是如此 SELECT
读取一些具有“之前”值的行和其他具有“之后”值的行。只需要设计一种情况
- 选择查询读取行的值
R1
并释放其S
锁 - 更新查询更新
R2
并采取X
锁 - 选择查询尝试读取
R2
并被阻止。 - 更新查询更新
R1
并采取X
锁。 - 更新事务提交,从而释放其锁并允许 Select 读取
R2
例如,如果 SELECT
和 UPDATE
使用不同的索引来定位感兴趣的行。
例子
CREATE TABLE T
(
X INT IDENTITY PRIMARY KEY,
Y AS -X UNIQUE,
Name varchar(10),
Filler char(4000) DEFAULT 'X'
)
INSERT INTO T (Name)
SELECT TOP 2500 'A'
FROM master..spt_values
现在在一个查询窗口中运行
DECLARE @Sum int
SELECT 'SET @@ROWCOUNT' WHERE 1=0
WHILE (@@ROWCOUNT = 0)
SELECT @Sum = SUM(LEN(Name))
FROM T
WHERE Y IN (-1, -2500)
HAVING SUM(LEN(Name)) = 3
这将无限循环运行。在另一次跑步中
UPDATE T
SET Name=CASE WHEN Name = 'A' THEN 'AA' ELSE 'A' END
这可能会停止另一个查询中的循环(如果没有,请重试),这意味着它必须已读取 A,AA
或者 AA,A
其他提示
提示 NOLOCK
等效于交易隔离水平 READ UNCOMMITTED
, ,仅限于一个表访问方法的范围。
这会使不承诺出现在您的结果集上的RU? 嗯,实际上是一个问题它不做什么. 。我会在下面解释。
好吧(我知道这是一个简化的简化)MSSQL(在其默认行为中)是锁定引擎 - 这意味着它使用锁定以一致的方式读取/写入数据。在这个过于简化的解释中,MSSQL使用两种锁: 共享锁 和 独家锁.
共享(S)锁定是一个锁定的锁,可以读取资源(可以是行,行甚至整个表)的锁定 - 但不能允许写入它。因此,如果交易T1将S锁定在R1行上,那么所有尝试读取R1的交易都将得到读取,但是尽管S锁定还活着,但没有人可以写信给R1。
独家(X)锁定是共享锁的对应物。它允许对资源的独家访问 - 除了获得X锁定的交易外,没有其他交易可以读取或写入。在上面的示例中,如果T1没有S锁,而是R1上的X锁 除T1以外,没有人可以读取或编写它。
那就是Teory。隔离水平尊重锁,并尊重它们的普遍性和特征。所有,除了 READ UNCOMMITTED
. 。简单地给出了一个 *(在此处将您的偏好率不好的口语放在读取的锁上) - 您仍然无法更新一行,另一项交易获得了X锁。它只是说: “我将阅读与查询计划相关的所有内容 - 无视它的锁。”并做。