如何解决SQL Server死锁 - 一旦更改顺序和缩短查询已用尽?

Ian*_*oyd 5 sql-server sql-server-2008-r2 database-deadlocks

我有两个假设的问题:

UPDATE BankAccounts SET HomePhone = '+1 252-555-0912' 
WHERE AccountNumber = 14400000619
Run Code Online (Sandbox Code Playgroud)

SELECT * FROM BankAccounts 
WHERE HomePhone = '555-1212'
Run Code Online (Sandbox Code Playgroud)

在没有额外索引的假设表上:

CREATE TABLE BankAccounts 
( 
   AccountNumber bigint NOT NULL PRIMARY KEY CLUSTERED,
   FirstName nvarchar(50) NOT NULL,
   MiddleName nvarchar(50) NULL,
   LastName nvarchar(50) NOT NULL,
   HomePhone varchar(50) NULL,
   IsClosed tinyint DEFAULT 0
)
Run Code Online (Sandbox Code Playgroud)

一切都会很棒.如果我添加一个索引HomePhone:

CREATE INDEX IX_BankAccounts_HomePhone ON BankAccounts 
( HomePhone)
Run Code Online (Sandbox Code Playgroud)

现在我的SELECT陈述可能是一个僵局的受害者:

Tranasction(进程ID 169)在锁资源上与另一个进程陷入僵局,并被选为死锁牺牲品.重新运行该交易.

共同的建议是:

  • 以相同的顺序访问表
  • 保持交易尽可能短

除了这种情况:

  • 我以相同的顺序访问(一)表(1选1是1)
  • 交易是单一的声明 ; 我不能比这更短

消除这种死锁的长期解决方案是什么?

我正在考虑将我的交易隔离级别更改为READ UNCOMMITTED(即消除完整性),但由于我实际上正在处理金融系统,因此我犹豫是否允许客户两次撤回其全部余额.

我能找到的唯一其他解决方案来自KB Article 83252:

SQL Server技术公告 - 如何解决死锁

...死锁是无法避免的.这就是为什么前端应用程序应该设计为处理死锁的原因.

在设计良好的应用程序中,前端应用程序应捕获1205错误,重新连接到SQL Server,然后重新提交事务.

我猜这说:"不能赢;不要试试"

还要别的吗?

Dam*_*ver 5

如果将 替换为列列表(或考虑所有列),然后将它们作为d columnsSELECT *添加到索引中,那么查询将不需要查询聚集索引来完成。然后总是可以运行完成。INCLUDESELECTSELECT

如果您不想这样做,那么我不会改变整个隔离级别,而是考虑锁定提示是否合适SELECT。无论是适当的提示NOLOCK还是READPAST您需要仔细考虑的内容(在理想的情况下,将有一种方法来指定READPAST但获取有关是否实际发生任何跳过行的信息)。

当然,也可以考虑SNAPSHOT 隔离(如果没有锁,就不会有死锁)。


(贪图惩罚的人也可以考虑TABLOCKXSELECT语句上使用。它将防止死锁并确保您读取一行(如果存在),但会造成并发性的巨大损失)


Rem*_*anu 3

无法获胜;不要尝试

这是正确的。您可能会看到读写死锁(没有死锁图只是我们的猜测)。逆序访问的发生正是因为您正在做正确的事情(即您确实拥有正确的索引)。即使您访问不同的密钥(查找一部手机,更新另一部手机),由于哈希冲突,您仍然在玩概率游戏。

您可以接受死锁或诉诸快照隔离模型。