【问题标题】:How to avoid deadlocks on simple update statements?如何避免简单更新语句的死锁?
【发布时间】:2013-05-01 14:41:24
【问题描述】:

我有一个表格来保存客户端的连接和断开时间。

ID        int
ClientID  int
BeginDate datetime
EndDate   datetime

当客户端连接时,会创建一条Session 记录,其EndDate 为空。

INSERT Session(ClientID, BeginDate, EndDate)
VALUES(@ClientID, GETDATE(), null)

当客户端断开连接时,我会更新会话,使其 EndDate 包含断开连接时间。

UPDATE Session
SET    EndDate = GETDATE()
WHERE  Id      = @SessionID

但这并不总是被调用。

因此,为确保同一客户端不会同时拥有多个会话,我在插入新会话之前运行此查询。

UPDATE Session
SET    EndDate     = GETDATE()
WHERE  ClientID    = @ClientID
 and   EndDate     is null

根据deadlock graph,当多个客户端同时连接时会导致死锁。

我不明白为什么这会导致死锁,
我什至不明白为什么该查询需要锁定。
我做错了什么?

【问题讨论】:

    标签: sql-server asynchronous sql-update relational-database database-deadlocks


    【解决方案1】:

    每次插入 EndDate 之前都更新它似乎不是一个好主意。即使没有完成更新,您也可能会锁定表。您应该在更新之前检查给定客户端的 endate 是否为空的任何会话。您可以使用 NoLock 提示阅读 未提交的数据。

    执行此检查将最大限度地减少此查询导致的锁定。预防胜于治疗:)

    【讨论】:

    • +1,我现在只选择 ID with (nolock) 并仅在需要时更新表格。它就像一个魅力,谢谢。
    【解决方案2】:

    更新语句会产生 X 锁,因为该语句甚至可能是页锁或表锁,您可以尝试选择打开会话的 id 并在之后使用 in-clause 执行更新,这更有可能只锁定那些行。这也取决于您的隔离级别。

    方式不能打开多个会话,如果(我猜它是一个 Web 应用程序)用户使用 2 个不同的 Web 浏览器打开该站点怎么办。

    【讨论】:

    • +1,谢谢。死锁图表明它是一个页面锁,是的,首先选择 ID。因为他的nolock推荐,我接受了ARS的回答。在使用之前,select 查询也会导致死锁。关于会话,它不是一个网站,它是我们的 GPRS 设备连接的 Windows 服务,所以一个设备不能同时连接多次。
    猜你喜欢
    • 2020-10-25
    • 1970-01-01
    • 2017-10-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-07
    • 2012-08-14
    • 1970-01-01
    相关资源
    最近更新 更多