【问题标题】:A very strange deadlock on key lock in SQL ServerSQL Server 中键锁的一个非常奇怪的死锁
【发布时间】:2014-11-12 04:34:58
【问题描述】:

很奇怪,当有 2 个连接在不同的行上运行以下事务时,会检测到死锁。为什么查询优化器要求事务 T1 有资源 KEY2,即事务 T2 更新的行?

KEY1 (ROW 1) KEY: 5:72057594048348160 (150fa2746afc)

KEY2 (ROW 2) KEY: 5:72057594048348160 (1bec117e39ae)

2 个事务同时更新不同的行。假设它们不会相互干扰 UPLOCK 和 ROWLOCK?

BEGIN TRAN
SELECT * FROM TABLE WITH(UPLOCK, ROWLOCK) WHERE PK_COL1 = ? and PK_COL2 = ?
UPDATE TABLE SET COL3 = ? WHERE PK_COL1 = ? and PK_COL2 = ?
END

以下是死锁列表

<process id="process10e7502c8" taskpriority="0" logused="0" waitresource="KEY: 5:72057594048348160 (150fa2746afc)" 
....
<process id="process10e750988" taskpriority="0" logused="0" waitresource="KEY: 5:72057594048348160 (1bec117e39ae)" 
...
<resource-list>
  <keylock hobtid="72057594048348160" dbid="5" objectname="" indexname="" id="locka6b73300" mode="U" associatedObjectId="72057594048348160">
    <owner-list>
      <owner id="process10e750988" mode="U" />
    </owner-list>
    <waiter-list>
      <waiter id="process10e7502c8" mode="U" requestType="wait" />
    </waiter-list>
  </keylock>
  <keylock hobtid="72057594048348160" dbid="5" objectname="" indexname="" id="locka5319b80" mode="U" associatedObjectId="72057594048348160">
    <owner-list>
      <owner id="process10e7502c8" mode="U" />
    </owner-list>
    <waiter-list>
      <waiter id="process10e750988" mode="U" requestType="wait" />
    </waiter-list>
  </keylock>
</resource-list>

这是发生死锁时的 sp_lock 结果

spid    dbid    ObjId   IndId   Type    Resource    Mode    Status
51  5   0   0   DB                                      S   GRANT
52  6   0   0   DB                                      S   GRANT
53  4   0   0   DB                                      S   GRANT
54  5   0   0   DB                                      S   GRANT
54  5   1941581955  0   TAB                                     IX  GRANT
54  5   1941581955  1   KEY (1bec117e39ae)                      U   GRANT
54  5   0   0   MD  4(6:0:0)                            Sch-S   GRANT
54  5   1941581955  1   KEY (150fa2746afc)                      U   WAIT
54  5   1941581955  1   PAG 1:73626                             IU  GRANT
57  5   0   0   DB                                      S   GRANT
58  6   0   0   DB                                      S   GRANT
58  6   0   0   APP 16384:[Repl-LogRead]:(04dddec9)     X   GRANT
59  5   0   0   DB                                      S   GRANT
60  6   0   0   DB                                      S   GRANT
61  5   0   0   DB                                      S   GRANT
62  5   0   0   DB                                      S   GRANT
63  4   0   0   DB                                      S   GRANT
64  4   0   0   DB                                      S   GRANT
65  5   0   0   DB                                      S   GRANT
65  5   1941581955  1   KEY (1bec117e39ae)                      U   WAIT
65  5   1941581955  1   PAG 1:73626                             IU  GRANT
65  5   0   0   MD  4(6:0:0)                            Sch-S   GRANT
65  5   1941581955  1   KEY (150fa2746afc)                      U   GRANT
65  5   1941581955  0   TAB                                     IX  GRANT
66  6   0   0   APP 16384:[DC1ISGSD03\I]:(152e28ac)     X   GRANT
66  6   0   0   DB                                      S   GRANT
67  1   1131151075  0   TAB                                     IS  GRANT
69  5   0   0   DB                                      S   GRANT

如果我添加一个与主键创建的聚集索引具有相同列和排序的非聚集索引,死锁问题就会消失。但是为什么更新聚集索引键上的一行需要其他聚集索引键上的更新锁呢?

如果我有任何误解,请纠正我。任何答案将不胜感激。

表模式描述如下 SQL 脚本

create table [dbo].[TABLE1]
(
[PK_COL1] char(10) not null,
[PK_COL2] char(10) not null,
[COL3] char(10) not null,
PRIMARY KEY ([PK_COL1],[PK_COL2])
);

【问题讨论】:

  • 米奇,答案不行。
  • 聚集索引碎片严重吗?
  • 不应该。它只有不到 100 条记录。我尝试重建索引,但死锁问题仍然发生。
  • 请发布您的完整 TSQL 表架构定义。

标签: sql-server


【解决方案1】:

添加READPAST 提示:

BEGIN TRAN
    SELECT * FROM TABLE WITH(UPDLOCK, ROWLOCK, READPAST) 
    WHERE PK_COL1 = ? and PK_COL2 = ?
    UPDATE TABLE SET COL3 = ? WHERE PK_COL1 = ? and PK_COL2 = ?
COMMIT TRAN

正如@CJBS 在 cmets 中指出的那样,提示的使用应仅限于需要它们并且您完全了解其后果的情况。

上面示例中的提示出现在使用表作为队列的示例中,您希望选择单行,对其保持锁定,在单独的语句中更新(例如删除处理后的行),而且还允许其他读者读取超过持有更新锁的行。

【讨论】:

  • 嗨,米奇。这不是针对你的。相反,这是对任何可能在不了解其含义的情况下使用表格提示的人的警告。
【解决方案2】:

我终于发现需要非聚集索引来避免死锁。

CREATE NONCLUSTERED INDEX [TABLE1_IDX1] ON [TABLE1]([PK_COL1], [PK_COL2]);

【讨论】:

    猜你喜欢
    • 2015-11-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多