Q1:是的,至少在理论上这是完全可能的。 read committed 只是保证您不会读取脏数据,它不承诺一致性。在读取提交级别,共享锁在数据被读取后立即释放(不是在事务结束时,甚至不是在语句结束时)
Q2:是的,如果read_committed_snapshot 开启,这个问题的答案将会改变。然后,您将获得语句级别的一致性保证。我发现很难找到明确说明这一点但引用“Microsoft SQL Server 2008 Internals”的第 648 页的在线资源
RCSI 中的语句可以看到一切
在开始之前提交
陈述。中的每个新语句
交易获取最新的
提交的更改。
还有see this MSDN blog post
设置脚本
CREATE TABLE person
(
id int primary key,
name varchar(50)
)
INSERT INTO person
values(1, 'foo');
连接 1
while 1=1
update person SET name = CASE WHEN name='foo' then 'bar' ELSE 'foo' END
连接 2
DECLARE @Results TABLE (
id int primary key,
name1 varchar(50),
name2 varchar(50))
while( NOT EXISTS(SELECT *
FROM @Results) )
BEGIN
INSERT INTO @Results
Select p1.id,
p1.name,
p2.name
from person p1
INNER HASH join person p2
on p1.id = p2.id
WHERE p1.name <> p2.name
END
SELECT *
FROM @Results
结果
id name1 name2
----------- ----- -----
1 bar foo
查看 Profiler 中的其他连接类型,在此特定查询的 merge 连接或 nested loops 计划下似乎不会出现此问题(在获取所有锁之前不会释放锁),但重点仍然是read committed 只是保证您不会读取脏数据,它不承诺一致性。实际上,对于您发布的确切查询,您可能不会遇到此问题,因为默认情况下 SQL Server 不会选择此连接类型。但是,您只需要依靠实现细节来产生您想要的行为。
注意:如果您想知道为什么某些行级别的 S 锁似乎丢失了,这里是 an optimisation explained here。