【问题标题】:when/what locks are hold/released in READ COMMITTED isolation level何时/什么锁在 READ COMMITTED 隔离级别中被持有/释放
【发布时间】:2012-07-04 21:42:06
【问题描述】:

我正在尝试了解 SQL Server 中的隔离/锁定。

我在 READ COMMITTED 隔离级别(默认)中有以下场景

我们有一张桌子。

create table Transactions(Tid int,amt int)

with some records

insert into Transactions values(1, 100)
insert into Transactions values(2, -50)
insert into Transactions values(3, 100)
insert into Transactions values(4, -100)
insert into Transactions values(5, 200)

现在从msdn我明白了

When a select is fired shared lock is taken so no other transaction can modify data(avoiding dirty read).. Documentation also talks about row level, page level, table level lock.我想到了以下场景

Begin Transaction

select * from Transactions

/*
some buisness logic which takes 5 minutes

*/

Commit

我想了解的是获取共享锁的持续时间以及哪个(行、页、表)。

只有在语句 select * from Transactions 运行时才会获取锁,或者在我们到达 COMMIT 之前,它会被获取整整 5 分钟以上。

【问题讨论】:

    标签: sql-server tsql locking isolation-level


    【解决方案1】:

    只有在select * from Transaction 运行时才会获得锁

    您可以使用以下代码检查它

    打开一个 sql 会话并运行此查询

    Begin Transaction
    
    select * from Transactions
    
     WAITFOR DELAY '00:05'
    /*
    some buisness logic which takes 5 minutes
    
    */
    
    Commit
    

    打开另一个 sql 会话并在查询下运行

    Begin Transaction
    Update Transactions
    Set = ...
    where ....
    commit
    

    【讨论】:

    • 我试过了,更新语句已完成,而读取事务处于挂起状态。可能所有内容都由 MVCC 解决,没有任何锁定已提交的读取。顺便说一句,我的 READ_COMMITTED_SNAPSHOT 设置为 OFF。也许共享锁只用于写锁,但它仍然允许读取。
    【解决方案2】:

    你问错问题了,你关心的是实现细节。您应该考虑和关心的是隔离级别的语义。 Kendra Little 有一张很好的海报来解释它们:Free Poster! Guide to SQL Server Isolation Levels

    您的问题应该改写为:

    从项目中选择 *

    问:我会看到哪些物品?
    A:所有承诺的项目

    问:如果有未提交的事务已插入/删除/更新项目会怎样?
    答:您的 SELECT 将阻塞,直到所有未提交的项目都已提交(或回滚)。

    问:如果在我运行上述查询时插入/删除/更新新项目会发生什么情况?
    A:结果未定。您可能会看到一些修改,不会看到其他一些修改,并且可能会阻塞,直到其中一些提交。

    READ COMMITTED 在您的语句完成后不做任何承诺,与事务的长度无关。如果您再次运行该语句,您将再次拥有与之前状态完全相同的语义,并且您之前看到的项目可能会更改、消失并出现新的项目。显然,这意味着在您选择后可以对项目进行更改。

    更高的隔离级别提供更强的保证:REPEATABLE READ 保证在您提交之前,您第一次选择的任何项目都不会被修改或删除。 SERIALIZABLE 添加了保证,在您提交之前,您的第二次选择中不会出现任何新项目。

    这是你需要了解的,不是实现机制是如何工作的。在你掌握了这些概念之后,你可能会询问实现细节。它们都在Transaction Processing: Concepts and Techniques 中进行了描述。

    【讨论】:

    • 在获取表锁而不是行锁时,实现细节很重要。
    【解决方案3】:

    你的问题很好。了解获取什么样的锁有助于深入了解 DBMS。在 SQL Server 中,在所有隔离级别(未提交读、已提交读(默认)、可重复读、可序列化)下,都会为写操作获取排他锁。

    无论隔离级别如何,事务结束时都会释放独占锁。

    隔离级别之间的差异是指获取/释放共享(读取)锁的方式。

    在未提交的读取隔离级别下,不会获取共享锁。在此隔离级别下,可能会发生称为“脏读”的并发问题(允许事务从已被另一个正在运行的事务修改但尚未提交的行中读取数据,因此可以回滚)。

    在 Read Committed 隔离级别下,为相关记录获取共享锁。当前指令结束时释放共享锁。此隔离级别可防止“脏读”,但由于其他并发事务可以更新记录,因此“不可重复读”(事务 A 检索一行,事务 B 随后更新该行,事务 A 稍后再次检索同一行. 事务 A 两次检索同一行但看到不同的数据)或“幻读”(在事务过程中,执行了两个相同的查询,并且第二个查询返回的行集合与第一个不同)可能会发生.

    在可重复读取隔离级别下,在事务期间获取共享锁。 “脏读”和“不可重复读”被阻止,但“幻读”仍然可能发生。

    在可序列化隔离级别下,在事务期间获取范围共享锁。上述并发问题均未发生,但性能大幅下降,并且存在发生死锁的风险。

    【讨论】:

      【解决方案4】:

      首先,只有在语句运行时才获取锁。 您的陈述分为两部分,假设很简单:

      select * from Transactions
      update Transactions set amt = xxx where Tid = xxx
      

      在 READ COMMITTED 隔离级别中什么时候/什么锁被持有/释放? 当select * from Transactions 运行时,没有获得锁。

      update Transactions set amt = xxx where Tid = xxx 之后将添加 X 锁用于更新/更新键,IX 锁用于页面/标签

      所有锁只有在提交/回滚后才会释放。这意味着在 trans 运行中不会释放任何锁。

      【讨论】:

        猜你喜欢
        • 2014-09-18
        • 2019-06-18
        • 2010-11-30
        • 1970-01-01
        • 2021-01-30
        • 1970-01-01
        • 2021-02-02
        • 1970-01-01
        • 2020-02-13
        相关资源
        最近更新 更多