use*_*910 4 sql-server deadlock sql-server-2016
最近在尝试同时在两个表上插入数据时,偶尔会遇到死锁。
以下是表结构
Create Table TableA
(
Id Bigint not null primary key ,
FieldA1 nvarchar(50)
)
go
Create Table TableB
(
Id Bigint not null primary key,
TableAId Bigint not null constraint FK_TableA Foreign Key (TableAId) References TableB(Id),
FieldB1 nvarchar(50)
)
go
CREATE type TableBParam as table
(
Id Bigint,
TableAId Bigint not null,
FieldB1 nvarchar(50)
)
--Deadlock was observed on the save query
go
CREATE PROC SaveTableB
(
@val [dbo].[TableBParam] READONLY
)
AS
BEGIN
SET NOCOUNT ON;
MERGE [dbo].[TableB] AS T
USING (SELECT * FROM @val) AS S
ON ( T.Id = S.Id)
WHEN MATCHED THEN
update set FieldB1 = S.FieldB1
WHEN NOT MATCHED THEN
insert(TableAId, FieldB1) Values(S.TableAId, S.FieldB1);
END
go
Run Code Online (Sandbox Code Playgroud)
在单个事务中,我试图通过调用各自的存储过程(SaveTableA 和 SaveTableB)来完成 tableA 和 Table B 上的数据插入。SaveTableA 与 SaveTableB 相同。
数据库已启用读提交快照隔离。想知道插入时如何发生死锁?从上图中我推断来自一个线程的 SaveTableB 是受害者,而不同线程上相同过程的其他实例则持有 TableA 主键的锁。
从这个参考我了解到这里的锁在索引的 Btree 上,基于参考https://technet.microsoft.com/en-us/library/ms189849(v=sql.105).aspx和死锁图以上。当锁定的资源位于聚集索引 BTree 节点本身时,
从上面的推论我很好奇理解以下
很想知道是否有可能在提到的这种情况下陷入僵局,试图了解如何处理它,以便我准备好解决它。感谢您帮助我了解这一点。
Dav*_*oft 11
MERGE
默认情况下,并发语句将死锁或产生 PK 违规,因为 的“扫描”阶段MERGE
是在没有限制性锁的情况下执行的。您需要添加一个锁定提示才能使其工作。请参阅为什么 TSQL MERGE 因主键违规而失败?不是原子的吗?由 David Browne 提供MERGE
锁定细节。
或者只是像这样修复它:
MERGE [dbo].[TableB] with (serializable) AS T
USING (SELECT * FROM @val) AS S
ON ( T.Id = S.Id)
WHEN MATCHED THEN
update set FieldB1 = S.FieldB1
WHEN NOT MATCHED THEN
insert(TableAId, FieldB1) Values(S.TableAId, S.FieldB1);
Run Code Online (Sandbox Code Playgroud)