【问题标题】:SQL Server deadlock and Data PagesSQL Server 死锁和数据页
【发布时间】:2014-07-15 20:12:50
【问题描述】:
  1. 如果表中的一行(没有主键)在对该行进行一些修改(更新查询)时被锁定,我假设首先在表上获取意图锁,然后在获取排他锁之前进行页面在行上。

    现在假设某个其他线程希望同时修改(更新查询)同一张表中的某些 other,然后 SQL Server 抛出以下错误:

    消息 1205,第 13 级,状态 45,第 1 行
    事务(进程 ID 65)与另一个进程在锁资源上死锁,并已被选为死锁牺牲品。重新运行事务。

    现在这个错误是否可能是同一张表的其他行在第一个查询的同一数据页中锁定的数据中的原因?

  2. 我知道数据页面还选择了我们不要求的其他数据。那么,如果我们在一个表中有一个主键,那么数据页还会选择其他数据还是只选择具有主键的那一行?

【问题讨论】:

  • 如果不知道每个 SPID 导致死锁所做的一切,很难说出您的场景中发生了什么。 deadlock example 会帮助你吗?
  • 死锁示例突出显示在同一行上进行的更新。我的发生在不同的行上。令我惊讶的是,我通过添加主键(之前没有主键)对表的设计进行了一些更改,并且错误 1205 消失了。我只是想知道为什么当我进行此更改时此错误消失了,因此我想知道我对第二个问题的理解是否正确?
  • 那是有道理的。如果没有 PK(或唯一索引),SQL 可能会获取比行锁更大的东西来执行更新。我找不到任何引用,但我相信在没有 PK(或唯一索引)的情况下,SQL 创建了一个底层机制来唯一标识每一行(pageid 与其他内容连接)。但这可能不足以防止页面锁、表锁等。

标签: sql sql-server


【解决方案1】:
  1. 可能是同一张表的另一行在第一个查询的同一数据页中锁定的数据中的原因?

没有。如果两个查询使用相同的粒度(例如行),那么它们将在高级别(表(分区)、页)获得兼容的意图锁,在低(行)级别获得不同的锁。即使它们使用不兼容的粒度,也不会发生死锁。可能会发生阻塞,但不会发生死锁。

我知道数据页面还会选择我们不要求的其他数据。那么如果我们在一个表中有一个主键,那么数据页还会选择额外的数据还是只选择具有主键的那一行?

这没有任何意义。您将逻辑(主键)与物理(数据页)混合在一起,而 data page also selects additional data that we do not request for 对我来说实际上是无法解析的。我只能推测您想说的是:

在组织为堆(无聚集索引)的表中,所有扫描都必须检查每一行以测试谓词。这会导致锁冲突。

当堆(没有聚集索引的表)上发生并发更新时如果没有要考虑的非聚集索引,则没有更新可能会死锁。所有更新都会以相同的顺序(堆物理分配顺序)扫描表,并且所有的锁都将以相同的顺序获取。更新可以阻塞,但不能死锁。

当并发更新发生在堆或组织为聚集索引的表上时,但存在非聚集索引,那么每次更新都可以使用一个(或多个)非聚集索引来定位更新的候选行。不使用相同谓词(即不同的 WHERE 子句)的更新可以以不同的顺序使用不同的 NC 索引。这些可能会死锁,因为获取锁的顺序会有所不同。

当以聚集索引组织的表上发生并发更新时,就会发生死锁,因为应用程序可以按导致死锁的顺序显式请求更新(Tran 1 更新键 A 然后 B,而 Tran 2 更新密钥 B 然后 A)。

发生死锁的方式还有很多,但这些是 UPDATE 与 UPDATE 死锁发生的基本方式。

如果您想获得有关 SQL Server always start by capturing the deadlock graph 中的死锁、任何死锁的答案。没有死锁信息,一切都是猜测。

PS。请不要将聚集的有组织的表称为“主键”。主键是一个逻辑概念。

【讨论】:

    【解决方案2】:

    SQL Server 中有多种锁,用于不同“粒度”的数据。

    在一个完美的世界中,如果你只更新一行,实际上它只能持有一个行锁,而任何其他行都不会被锁定。

    引擎保留将锁“升级”到更大集合(例如页面甚至整个表)的权利。
    如果您要更新范围,这可能是必要的。

    如果您说您有一个主键,它可能也是您的聚集索引键。并且它是查找要更新的行的标准...当然,您可能总是避免锁定太多行。
    除非您在表上定义了任何其他索引。
    然后,您仍然可能会对可能导致死锁的存储单元产生影响。

    -

    要了解您看到更改的原因,请记住在 SQL Server 中,表只是逻辑实体,而索引是“真正的”表。

    如果你根本没有索引,那么 db 引擎可能确实别无选择,只能一直锁定整个表,因为它没有基础只锁定一定范围的行。
    它需要一个相关的索引来创建锁 - 并保留其他行。

    一旦您可以说要更新键,例如“1 到 5”,那么 db 引擎就可以在这些键值上定义锁。 然后表的其余部分没有锁定!

    【讨论】:

    • 请注意,有PAGEEXTENT 锁(among others)。如果表足够大,这些类型的锁不会锁定整个表,从而为 SQL 提供了一些“选择”。
    • 如果你根本没有索引,db引擎可能确实别无选择,只能锁定整个表这是不正确的,可以使用RID单独锁定堆行.
    【解决方案3】:

    这不是一件容易回答的事情,因为多件事情会导致死锁,而且它们在不同的时间可能会有所不同。 imo 调查的最佳方法是使用 SQL Server Profile 并查找死锁事件并捕获死锁图from MSDN。这个article 包含有关 SQL Server 如何进行锁定的完整详细信息,以及有关死锁和避免死锁的一些附加信息。

    回答您的问题:

    1. 是的,两行可能会死锁等待对方(转换死锁)
    2. 取决于您的查询要求。每个查询都指定了它想要的列,所以这在您的控制之中。如果你只要求PK,那就是你会得到的。

    【讨论】:

    • 如果它们都在等待同一资源,则可能发生转换死锁。如前所述,线程正在更新不同的资源。关于第二个,您的意思是即使数据页也可能给出带有主键的行而不是任何其他附加行?
    • 不清楚你在问什么。数据页的结构因您拥有的索引类型和您拥有的数据类型而异。
    【解决方案4】:

    两个进程想访问对方进程锁定的资源,互相阻塞。

    以下博客是关于死锁预防的:

    http://www.sqlusa.com/bestpractices/deadlock/

    【讨论】:

      猜你喜欢
      • 2015-04-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-11
      相关资源
      最近更新 更多