【问题标题】:Avoiding deadlock when updating table更新表时避免死锁
【发布时间】:2017-10-18 03:01:07
【问题描述】:

我有一个 3 层应用程序,并在客户端缓存了数据,因此我需要知道服务器上的数据何时更改以保持缓存同步。

所以我在表格中添加了一个“lastmodification”字段,并在数据更改时更新此字段。但是,如果子行(使用 FK)被修改,必须更新一些“父”最后修改行。
从主表中获取 MAX(lastmodification),从相关表中获取 MAX,然后这几个值的 MAX 正在工作,但有点慢。 我的意思是:

MAX(MAX(MAIN_TABLE), MAX(CHILD1_TABLE), MAX(CHILD2_TABLE))

所以我切换并添加了一个触发器到这个表,以便它更新 TBL_METADATA 表中的一个字段:

CREATE TABLE [TABLE_METADATA](
    [TABLE_NAME] [nvarchar](250) NOT NULL,
    [TABLE_LAST_MODIFICATION] [datetime] NOT NULL

现在相关表可以通过更新元数据表中的最后修改时间来更新“主”表的最后修改时间。 获取最后的修改现在很快

但是......现在我遇到了与更新此表相关的随机死锁。

这是由于 2 个事务在不同的步骤修改 TABLE_METADATA,然后相互锁定。

我的问题:你有没有办法在不锁定行的情况下保持这个 lastmodification 更新? 就我而言,我真的不在乎:

  • 即使事务回滚,lastmodification 也会保持更新
  • “脏”最后修改(已更新但尚未提交)是 被新值覆盖

事实上,我真的不需要这些更新在事务中,但由于它们是由触发器执行的,所以它会自动在当前事务中。

感谢您的帮助

【问题讨论】:

  • 您是否尝试过使用类似 NOLOCK 的提示?这将在不带任何锁的情况下更新,因此不会导致死锁
  • 我知道选择的 NOLOCK 提示,但不知道更新时可以使用?我去查一下,谢谢
  • 没关系,我忘了它只适用于 SELECT... 试试看,你会得到:“对于 INSERT、UPDATE、DELETE 或 MERGE 语句的目标表,不允许使用 NOLOCK 和 READUNCOMMITTED 锁定提示。 "

标签: sql sql-server database-deadlocks


【解决方案1】:

据我所知,您无法阻止 U 形锁。但是,您可以尝试使用with (rowlock) 将锁的数量减少到最低限度。 这将告诉查询优化器在更新行时一一锁定行,而不是使用页或表锁定。

您还可以在连接到正在更新的表的表上使用with (nolock)。另一种方法是使用set transaction isolation level read uncommitted。 但请小心使用此方法,因为您可能会创建损坏的数据。

例如:

update mt with (rowlock)
    set SomeColumn = Something
    from MyTable mt
        inner join AnotherTable at with (nolock)
        on mt.mtId = at.atId

您还可以将with (rowlock)with (nolock)/set transaction isolation level read uncommitted添加到经常读写同一张表的其他数据库对象中,以进一步降低发生死锁的可能性。

如果死锁仍在发生,您可以通过自连接来减少目标表上的读锁定,如下所示:

update mt with (rowlock)
    set SomeColumn = Something
    from MyTable mt
    where mt.Id in (select Id from MyTable mt2 where Column = Condition)

有关表格提示的更多文档可以找到here.

【讨论】:

  • 这是一个非常有趣的答案。我从未见过这样的建议,即如果与未提交的读取相结合,更新会更快(涉及大量共享锁和排他锁)将拥有更少的锁。当您进行更新时,您真的想确保您正在更新“正确”的行。与 nolock 结合使用(这是一个未提交的读取,这意味着您可能正在使用永远不会提交的更改)可能会导致一些非常有趣的损坏数据。
  • 你说得很好,我没有提到这一点,因为 OP 说他们并不真正关心“脏”更新/读取。我将添加一小部分关于此的内容。
  • 我们不应该仅仅因为用户说他们不在乎就鼓励不良行为。很少有适合读取未提交的情况。
  • @JoshB 问题是我不关心它,而只关心 lastmodification 值。但是更改事务级别不仅会影响lastmodification查询,还会影响在这个事务中执行的所有查询,而且确实在这种情况下我会遇到一致性问题,所以我不能这样做。我将阅读有关行锁提示的文档。
猜你喜欢
  • 2020-10-25
  • 1970-01-01
  • 2017-12-17
  • 1970-01-01
  • 1970-01-01
  • 2022-11-27
  • 2019-12-06
  • 2012-10-30
  • 1970-01-01
相关资源
最近更新 更多