【问题标题】:Transaction isolation levels and subqueries事务隔离级别和子查询
【发布时间】:2014-05-12 15:24:13
【问题描述】:

如果我们有一个带有子 SELECT 的 UPDATE,子查询是否可以在 READ COMMITTED 隔离下并发执行?

换句话说,以下是否存在竞争条件:

update list set [state] = 'active' 
where
    id = (select top 1 id from list where [state] = 'ready' order by id)

换句话说,如果许多连接同时执行此 SQL,我们能否保证每次调用实际上更新一行(只要存在“就绪”状态的行)?

【问题讨论】:

  • 将多个操作放入单个语句中对并发性毫无帮助。语句不提供与并发相关的保证。
  • 这是不正确的。在 READ COMMITTED 下,一次为一个 SELECT 语句获取和释放共享锁。但我不确定该示例采用什么顺序锁。我也很高兴将 begin tran 和 commit 放在它周围,尽管我很确定这没有什么区别,因为事务永远不会少于单个语句。
  • 没有。 READ COMMITTED 尽快释放锁,通常在每一行之后。对于没有未提交数据的页面,它甚至根本不需要锁。这是一个鲜为人知的优化。 “我也很高兴将 begin tran 和 commit 放在一起”这是另一个误解,认为这会改变任何事情。在自动提交模式下,语句仍然在隐式事务下运行。在这方面你是对的。
  • 我想知道为什么 MSDN 文档会如此明确地指出“因为共享锁 [in REPEATABLE READ] 被持有到事务结束而不是在每个语句结束时被释放 [强调我的],并发低于默认的 READ COMMITTED 隔离级别。”
  • 这是一个关于 REPEATABLE READ 的声明,它永远使用 S-locks 来确保数据是稳定的。 READ COMMITTED 不保证数据稳定。它几乎没有任何保证。

标签: sql-server concurrency isolation-level


【解决方案1】:

答案是,存在竞争条件,两个事务可能同时执行子查询,导致同一行随后被更新两次。

这可以通过将更新重写为来解决

update TEMP 
set [state] = 'active' 
from 
    (select top 1 * from list where [state] = 'ready' order by id) TEMP

坦率地说,我不知道为什么会有所不同,但确实如此。 SQL Server 现在将在执行子查询时采用更新锁(“意图更新”),防止并发事务选择同一行。

【讨论】:

    猜你喜欢
    • 2011-09-30
    • 1970-01-01
    • 2011-02-24
    • 2015-07-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-26
    相关资源
    最近更新 更多