【问题标题】:Read Committed Isolation level in SQL server for a single statement读取 SQL Server 中单个语句的已提交隔离级别
【发布时间】:2011-01-23 05:27:16
【问题描述】:

说,我有一个人表,它只有 1 行 -

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:如果在db上设置set read_committed_snapshot on,问题的答案会改变吗?

【问题讨论】:

    标签: sql-server transactions read-committed-snapshot


    【解决方案1】:

    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

    【讨论】:

    • 您能否详细说明为什么第二季度的答案会改变?我看到它发生的方式是:在时间 T1,'from' 尝试从表中读取行,但它被'update' 锁定,因此它读取的是旧版本('foo');在时间T2,更新完成并释放锁;在时间 T3,join 尝试读取行,并且该行未锁定,但具有新值('bar'),因此读取它以显示不一致的结果。这是可能的,还是保证一旦读取了旧版本的行,就永远不会在 read_committed_snapshot 模式下读取新版本?非常感谢。
    • @Raghu - 在 RCSI 模式下,可以在同一个事务中读取两个不同的值(如果事务有多个语句),但不能在同一个语句中读取。 RCSI 中的语句会看到在语句开始之前提交的所有内容。事务中的每个新语句都会获取最近提交的更改。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-08-18
    • 1970-01-01
    • 2014-01-03
    • 1970-01-01
    • 1970-01-01
    • 2014-02-16
    • 1970-01-01
    相关资源
    最近更新 更多