Rag*_*dda 6 sql-server transactions read-committed-snapshot
说,我有一个人桌,它只有一排 -
id = 1, name = 'foo'
Run Code Online (Sandbox Code Playgroud)
在一个连接上
select p1.id, p1.name, p2.name
from person p1
join person p2 on p1.id = p2.id
Run Code Online (Sandbox Code Playgroud)
在另一个连接上同时:
update person set name = 'bar' where person.id = 1
Run Code Online (Sandbox Code Playgroud)
Q1:是否有可能,因为我的select会根据更新语句的时间返回这样的结果:
id = 1, p1.name = 'foo', p2.name = 'bar'
Run Code Online (Sandbox Code Playgroud)
这两个连接都不使用显式事务,并且都使用默认事务隔离级别READ COMMITTED.
问题实际上是帮助我理解,在语句完成之前,在sql语句开头获取的锁是否继续存在,或者语句是否有可能释放锁并重新获取锁的锁如果在同一语句中使用两次行?
Q2:如果set read_committed_snapshot on在数据库上设置问题,问题的答案是否会改变?
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');
Run Code Online (Sandbox Code Playgroud)
while 1=1
update person SET name = CASE WHEN name='foo' then 'bar' ELSE 'foo' END
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
id name1 name2
----------- ----- -----
1 bar foo
Run Code Online (Sandbox Code Playgroud)
查看Profiler中的其他连接类型,似乎在这个特定查询的merge连接或nested loops计划下都不会出现此问题(在获取所有锁之前没有锁被释放),但重点仍然是read committed只保证您不读取脏数据对一致性没有任何承诺.在实践中,您可能不会因为您发布的确切查询而遇到此问题,因为SQL Server默认情况下不会选择此连接类型.但是,您只需依靠实现细节来生成所需的行为.
注意:如果您想知道为什么某些行级别S锁似乎缺失,这是一个在此解释的优化.