【问题标题】:Update to Exclusive lock conversion更新排他锁转换
【发布时间】:2013-08-01 18:57:37
【问题描述】:

SQL Server 2008R2 - ReadCommitted 隔离级别

我正在尝试准确计算 SQL Server 何时将更新锁转换为独占锁。例如,我有表 dbo.TableA。 dbo.TableA 有两列 PKCol1 和 NCCol2。 PKCol1 是一个聚集索引,而 NCCol2 上有一个非聚集索引。如果我要执行

BEGIN TRAN  

DELETE
FROM dbo.TableA 
WHERE NCCol2 = 1

COMMIT TRANSACTION 

如果优化器选择扫描 NCCol2 来查找所有候选记录,那么非聚集索引运算符是否会扫描索引中的所有记录。将更新锁添加到每个候选记录,直到它扫描了整个索引,然后聚集索引删除运算符将这些锁转换为排他锁并删除。

或者非聚集索引操作符是否会依次扫描每条记录,为候选记录添加更新锁,评估该行是否匹配以及是否将更新锁转换为排他锁。

基本上哪个操作员将更新锁转换为排他锁,一旦扫描识别出记录是匹配的非聚集索引扫描,或者一旦候选行被识别并传递给它,就会删除聚集索引?

网上书告诉我

更新(U)

用于可以更新的资​​源。防止在多个会话读取、锁定和稍后可能更新资源时发生的常见死锁形式

独占 (X)

用于数据修改操作,例如 INSERT、UPDATE 或 DELETE。确保不能同时对同一资源进行多次更新。

附加信息 1

我实际上是在调查优化器选择以下计划后发生在下面的非唯一非聚集 INT Index2 上的死锁

死锁翻译为

  1. 受害者在 dbo.Table1 Index2 row1 上获取了更新锁
  2. 所有者对 dbo.Table1 Index2 row2 进行了排他性锁定
  3. 受害者在 dbo.Table1 Index2 row2 上等待更新锁
  4. 所有者等待 dbo.Table1 Index2 row1 上的更新锁

据我了解,执行计划中的每个运算符都是按照从右到左从上到下的顺序完整执行的。但我也理解,更新锁仅在 UPDATE/INSERT 或 DELETE 期间转换为独占锁,即聚集索引删除操作符。因此,我不确定为什么步骤 2 中的所有者在 Index2 row2 上有一个排他锁,这表明它在聚集索引删除步骤,但仍在等待更新锁,这表明它也是非聚集索引扫描步骤。怎么可能同时在这两个步骤?

但是,如果您认为在索引扫描期间同时使用了更新锁和排他锁,那么这种死锁会更有意义。

在重新编译后,优化器选择在聚集索引上寻找没有问题

@Bogdan Sahlean 和 @brian - 非常感谢您的帮助和建议。

【问题讨论】:

    标签: sql sql-server sql-server-2008-r2 locking


    【解决方案1】:

    上下文:

    -DELETE 语句的目标表有一个唯一的聚集索引(PK)和一个非聚集索引。

    -聚集索引的键是IDA

    -非聚集索引的键是NCCol2

    -TableA内容:

    -- Clustered index (C index)
    NCCol2 IDA lockhash (these values are the "identifier" of locks)
    ------ --- --------------
    1      11  (29cf3326f583)
    2      22  (31178495a25a)
    
    -- Non-Clustered index (NC index)
    NCCol2 lockhash
    ------ --------------
    1      (8194443284a0)
    2      (61a06abd401c)
    

    在这种情况下:

    1. SQL Server 将选择一个Index Seek(在具有NCCol2 键的非聚集索引上)来查找记录,并选择一个Clustered Index Delete 运算符来删除记录:

    2. Index SeekNCCol2)运算符使用此谓词(NNCol2=1)查找记录,

    3. 1234563 ),
    4. 然后之前聚集索引记录上的U锁(lockhash (29cf3326f583))被转换为X锁和非聚集索引记录上的U锁(lockhash (8194443284a0))被转换,也可以到X lock。

    5. 两个索引中的记录都被删除(在我看来,这是删除这些记录的时刻)并且之前的X锁被释放。

    6. 使用非聚集索引查找另一行 > 转到第 2 步。

    您可以使用 SQL Profiler(或服务器跟踪、扩展事件)拦截这些事件(Lock:AquiredLock:Released):

    TLTR: 索引查找(查找记录)> NC 索引记录上的 U 锁定 > C 索引记录上的 U 锁定 > U -> C 索引记录的 X > U -> NC 索引的 X记录 > 删除记录 > 重复(查找另一行)。

    编辑 1: Scan 可能有很多原因:

    1) 一些implicit conversions(正如 brian 指出的)由类型优先级生成(如果数据库配置为Simple Parametrization1 将具有与列类型相同的类型,则1 将是TINYINT数据库配置为Force Parametrization)。

    2) 可以禁用索引。

    3) 它是过滤索引 (link) 或者是在计算列上定义的索引并且设置 (link: SET Option Requirements) 不合适。

    4) 这是一个过滤索引,并且该数据库已激活Parametrization Forced 设置。

    5) [低概率] 这是一个小表,并且(出于某种原因)SQL Server 选择Scan 而不是Seek

    【讨论】:

    • +1 表示内部@Bogdan。只要 NCCol2 与它被比较的相等值的类型相同,这将成立。例如,如果 NCCol2 是 varchar(25) 并且相等值是 int,则它将是一次 nc 扫描,因为数据类型优先级要求 varchar 端在比较之前隐式转换为 int。如果您没有考虑到这一点,那么您显然与比我更聪明的开发人员合作。
    • @brian:谢谢。我同意,但是在我的答案中添加有关隐式/显式转换的信息并不是我的意图。 Jonathan Kehayias 有一篇关于这个主题的非常好的博文:link
    • 再次+1。同意,但 OP 直接跳入优化器选择扫描。这让我想知道他/她试图理解的问题中是否存在数据类型不匹配。要么 NCCol2 = 1 要么具有非常低的选择性,并且扫描比查找便宜。不要误会我的意思,你在解释锁升级方面做得再好不过了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-29
    • 1970-01-01
    • 1970-01-01
    • 2013-03-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多