说,我有一个人表,它只有一行 -
id = 1, name = 'foo'
在一个连接上
select p1.id, p1.name, p2.name
from person p1
join person p2 on p1.id = p2.id
同时在另一个连接上:
update person set name = 'bar' where person.id = 1
Q1:是否有可能,我的选择会根据更新声明的时间返回这样的结果:
id = 1, p1.name = 'foo', p2.name = 'bar'
两个连接都不使用显式事务,并且都使用默认事务隔离级别READ COMMITTED。
问题实际上是帮助我理解,在语句完成之前,sql语句开头获取的锁是否继续存在,或者语句是否有可能释放锁并重新获取锁如果在同一语句中使用两次,则为同一行?
Q2:如果在数据库中设置了set read_committed_snapshot on
,问题的答案会改变吗?
答案 0 :(得分:9)
Q1:是的,这至少在理论上是完全可能的。 read committed
只是保证你不会读取脏数据,它不会对一致性做出承诺。在读取提交级别时,只要读取数据就会释放共享锁(不是在事务结束时甚至在语句结束时)
Q2:是的,如果read_committed_snapshot
开启,这个问题的答案就会改变。然后,您将获得语句级别一致性。我发现很难找到一个明确说明这一点的在线资源,但引用了“Microsoft SQL Server 2008 Internals”的p.648
RCSI的声明可以看到一切 在开始之前承诺 声明。每个新的声明在 交易回升最近 承诺的改变。
CREATE TABLE person
(
id int primary key,
name varchar(50)
)
INSERT INTO person
values(1, 'foo');
while 1=1
update person SET name = CASE WHEN name='foo' then 'bar' ELSE 'foo' END
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。