1

我们在保存订单履行的事务表中使用聚集列存储索引。该表由不同的会话定期更新。但是,每个会话都特定于订单作业编号,因此他们不会尝试同时更新同一行。但是,由于会话之间的以下情况,我们面临死锁问题。

  • 行组锁定和页面锁定
  • 行组锁定和行组锁定

这并不特定于存储过程。这是由于作为订单履行的一部分,多个存储过程按顺序一个接一个地更新此表。

表的示例模式非常简单:

CREATE TABLE OrderFulfillments
(
    OrderJobNumber           INT NOT NULL,
    FulfilledIndividualID    BIGINT NOT NULL,
    IsIndividualSuppressed   BIT NOT NULL,
    SuppressionReason        VARCHAR(100) NULL
)

我已经给出了示例死锁图供您参考。请让我知道,我可以采取什么方法来避免这种僵局情况。我们需要此表中的聚集列存储索引,因为我们正在执行聚合操作以查看个体已经完成了多少次。如果没有列存储索引,它可能会更慢。

在此处输入图像描述

4

2 回答 2

2

在我的例子中,死锁场景是由于发生了锁升级,因为一些实现非常大,在 10,000 或 100k 范围内,它导致锁升级发生在行组级别,在某些情况下,页面级别。

我解决了这个问题,方法是在事务的最开始有一个临时表,并在临时表上进行更新,最后将与临时表相关的履行信息插入其中OrderFulfillments。临时表OrderFulfillments也使用它来查看个人已经完成了多少次。但是,它是顶部的共享锁而不是排他锁。

通过使用临时表,每个会话都在处理自己的副本,并发问题得到解决。

于 2017-07-28T19:06:26.460 回答
1

您假设 NOLOCK 与没有锁定相同......这是不正确的。

NOLOCK 相当于 READUNCOMMITTED。

• READUNCOMMITTED 和 NOLOCK 提示仅适用于数据锁。

所有查询,包括带有 READUNCOMMITTED 和 NOLOCK 提示的查询,都会 在编译和执行期间获取 Sch-S(模式稳定性)锁。因此,当并发​​事务在表上持有 Sch-M(模式修改)锁时,查询会被阻止。

例如,数据定义语言 (DDL) 操作在修改表的架构信息之前获取 Sch-M 锁。

任何并发查询,包括使用 READUNCOMMITTED 或 NOLOCK 提示运行的查询,在尝试获取 Sch-S 锁时都会被阻止。 相反,持有 Sch-S 锁的查询会阻止尝试获取 Sch-M 锁的并发事务。

不能为插入、更新或删除操作修改的表指定 READUNCOMMITTED 和 NOLOCK。SQL Server 查询优化器忽略 FROM 子句中适用于 UPDATE 或 DELETE 语句的目标表的 READUNCOMMITTED 和 NOLOCK 提示。

您可以通过使用以下任一方法来最大程度地减少锁定争用,同时保护事务免受未提交数据修改的脏读:

• READ_COMMITTED_SNAPSHOT 数据库选项设置为ON 的READ COMMITTED 隔离级别。

• SNAPSHOT 隔离级别。有关隔离级别的详细信息,请参阅 SET TRANSACTION ISOLATION LEVEL (Transact-SQL)。

https://docs.microsoft.com/en-us/sql/t-sql/queries/hints-transact-sql-table

  • 如果说,选择语句需要 UPDATE 同时修改的整个页面,则了解索引的结构可能会导致阻塞。

  • 在测试时限制变量。

  • 考虑将您的 DML 拆分为多个部分。您可能会找到执行表数据并发修改的最佳范围。

于 2017-07-27T22:12:29.247 回答