为啥 UPDATE 在不相关的行上阻止 SELECT?

Posted

技术标签:

【中文标题】为啥 UPDATE 在不相关的行上阻止 SELECT?【英文标题】:Why UPDATE blocks SELECT on unrelated rows?为什么 UPDATE 在不相关的行上阻止 SELECT? 【发布时间】:2010-10-24 20:49:09 【问题描述】:

拥有由脚本 [1] 定义的表,我在 SSMS 的 2 个窗口中执行脚本

--1) first in first SSMS window
set transaction isolation level READ UNCOMMITTED;
begin transaction;
update aaa set Name ='bbb' 
    where id=1;
-- results in "(1 row(s) affected)"
--rollback

1) 之后

--2)after launching 1)
select * from aaa --deleted comments
where id<>1
--is blocked

独立于 1) 窗口中的事务隔离级别,2) 中的 SELECT 被阻止。 为什么?

UPDATE 的隔离级别对其他事务的语句有影响吗?

最高隔离级别是2)中默认的READ COMMITTED。 没有归属范围锁,SELECT 应该遭受 COMMITTED READS (NONREPEATABLE READs) 和 PHANTOM READS (Repeatable Reads) 问题[2]如何让它遭受痛苦?

如何在不阻塞 SELECT 的情况下进行 UPDATE?

[1]

CREATE TABLE aaa
(
    Id int IDENTITY(1,1) NOT NULL,
    Name  varchar(13) NOT NULL
)


insert into  aaa(Name) 
   select '111' union all 
   select '222' union all 
   select '333' union all 
   select '444' union all 
   select '555' union all 
   select '666' union all 
   select '777' union all 
   select '888'  

[2] 点击后复制粘贴或添加尾随 )http://en.wikipedia.org/wiki/Isolation_(database_systems)

更新: SELECT WITH(NOLOCK) 没有被阻塞...

更新2: 或者,相同的是,READ UNCOMMITTED

请注意,UPDATE 与 SELECT 行不同。 即使在相同的情况下,这种行为也与隔离级别的描述相矛盾 [2]

要点是:

假设我不知道还有谁将从同一个 (UPDATE-d) 表中选择,但与更新行无关 了解隔离级别 [2]

SQL Server 2008 R2 开发版

【问题讨论】:

您的意思是“--”带有提示吗? 如果使用行锁更新提示会怎样? (并假设它没有升级......) 【参考方案1】:

我相信这是因为您没有主键,我认为这会导致锁升级,从而阻止 SELECT。如果您在 ID 列中添加 PRIMARY KEY,您会注意到,如果您再试一次,SELECT 现在将返回其他 3 行 - 不需要 WITH (NOLOCK) 提示。

【讨论】:

【参考方案2】:

之后重复测试

--3)
create index IX_aaa_ID on aaa(id)

SELECT 2) 仍然被阻止

--4)
drop index IX_aaa_ID on aaa
create unique index IX_aaa_ID on aaa(id)
--or adding primary key constraint   

SELECT 2) 未被阻止

如果将2)修改为

--2b)
select * from aaa 
    where id=3 
    --or as
    --WHERE id=2 

表明即使没有任何索引或 PK,2b) 也不会被阻止。

虽然,2b),没有任何索引,在修改 1) UPDATE 以在可序列化下运行后被阻止 但不低于 REPEATABLE READ 或更低

--1c)  
set transaction isolation level serializable;
--set transaction isolation level REPEATABLE READ;

begin transaction;
update aaa set Name ='bbb' 
    where id=1;
--rollback

那么,看起来多行选择尝试获取不可共享锁?

更新: 好吧,在所有 SELECT 被阻塞的情况下,它都在等待获取 LCK_M_IS 了解这道菜的好理由

Update2:嗯,不是 UPDATE 锁在表上升级,而是 SELECT(共享)锁(当 SELECT 尝试读取多行时)升级为表锁,不能授予,因为表已经拥有独占(UPDATE)锁。

索引的存在与否与我的主要问题无关

我把这个话题的讨论转移到我提交的建议"Intent rowlocks should not be escalated to a table lock if a table already contains exclusive lock"

【讨论】:

以上是关于为啥 UPDATE 在不相关的行上阻止 SELECT?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的光标在 NextLine() 之后出现在错误的行上?

java.sql.SQLException:游标状态无效:标识符游标未定位在 UPDATE、DELETE、SET 或 GET 语句中的行上:;结果集为空

UPDATE 查询更改了没有 WHERE 子句但有 AND 子句的行 - 为啥?

我可以让 Spark 仅在必要的行上运行 UDF 吗?

当行不存在时,“SELECT FOR UPDATE”是不是会阻止其他连接插入?

在不同行上打印多个时间