如何解决 SQL Server 死锁 - 一旦更改顺序和缩短查询用尽?
Posted
技术标签:
【中文标题】如何解决 SQL Server 死锁 - 一旦更改顺序和缩短查询用尽?【英文标题】:How to resolve SQL Server deadlocks - once changing order and shortening queries is exhausted? 【发布时间】:2013-01-28 14:38:17 【问题描述】:我有两个假设的查询:
UPDATE BankAccounts SET HomePhone = '+1 252-555-0912'
WHERE AccountNumber = 14400000619
和
SELECT * FROM BankAccounts
WHERE HomePhone = '555-1212'
在没有额外索引的假设表上:
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
)
一切都会好起来的。如果我在HomePhone
上添加索引:
CREATE INDEX IX_BankAccounts_HomePhone ON BankAccounts
( HomePhone)
现在我的SELECT
声明可能成为僵局的受害者:
Transaction(进程 ID 169)与另一个进程在锁资源上死锁,并被选为死锁牺牲品。重新运行事务。
常见的建议是:
以相同的顺序访问表 尽可能缩短交易时间这种情况除外:
我正在以相同的顺序访问(一个)表(1 选择 1 是 1) 交易是一个单一语句;我不能比这更短了消除这种死锁的长期解决方案是什么?
我正在考虑将我的交易隔离级别更改为READ UNCOMMITTED
(即消除完整性),但因为我实际上是在处理一个金融系统,所以我很犹豫是否允许客户撤回他的整个余额两次。
我能找到的唯一其他解决方案来自KB Article 83252:
SQL Server 技术公告 - 如何解决死锁
...死锁无法避免。这就是为什么应该设计前端应用程序来处理死锁。
在设计良好的应用程序中,前端应用程序应捕获 1205 错误,重新连接到 SQL Server,然后重新提交事务。
我猜是在说:“不能赢;不要尝试”
还有什么?
【问题讨论】:
【参考方案1】:如果将SELECT *
替换为列列表(或考虑所有列),然后将它们作为INCLUDE
d columns 添加到索引中,则SELECT
查询将不需要查询聚集索引去完成。然后SELECT
可以一直运行到完成。
如果您不想这样做,那么与其更改整个隔离级别,我会考虑在SELECT
上使用locking hint 是否合适。您需要仔细考虑适当的提示是NOLOCK
还是READPAST
(在理想情况下,有一种方法可以指定READPAST
,但可以获得有关是否实际发生任何跳过行的信息)。
当然SNAPSHOT
isolation也可以考虑(如果没有锁就没有死锁)。
(贪婪的惩罚者也可以考虑在SELECT
语句上使用TABLOCKX
。它可以防止死锁并确保您读取存在的行,但会严重影响并发)
【讨论】:
在大型仓库应用程序中,我通过序列化有问题的操作“解决”了一些令人讨厌的死锁问题。这些操作中的每一个都尝试锁定相同的单个记录。我想这相当于你的 TABLOCKX? @VincentVancalbergh - 是的,这实际上是一种要求序列化的本地方式。【参考方案2】:赢不了;不要尝试
没错。您看到的可能是read-write 死锁(没有死锁图只是我们的推测)。 因为您正在做正确的事情(即,您确实有正确的索引),因此反向顺序访问恰好发生。即使您访问不同的密钥(查找一部手机,更新另一部手机),由于哈希冲突,您仍然在玩a game of probabilities。
您可以接受死锁或诉诸snapshot isolation 模型。
【讨论】:
以上是关于如何解决 SQL Server 死锁 - 一旦更改顺序和缩短查询用尽?的主要内容,如果未能解决你的问题,请参考以下文章