为啥相同事务的两个并行执行之间会出现死锁?
Posted
技术标签:
【中文标题】为啥相同事务的两个并行执行之间会出现死锁?【英文标题】:Why is there a deadlock between two parallel executions of identical transactions?为什么相同事务的两个并行执行之间会出现死锁? 【发布时间】:2022-01-24 02:16:50 【问题描述】:我有一个使用 SQL 事务来更新 SQL Server 上的 msdb 数据库的应用程序。该事务以不规则的时间间隔定期执行。使用 SQL Server 2012 (SP4)。一段时间后,SQL Server 报告 msdb.dbo.backupset 表出现死锁,唯一的参与者是执行上述 SQL 事务的进程
以下是事务的功能示例,将参数替换为示例值,以便可以在 msdb 上轻松执行。应用程序在每次调用事务时传递新参数。
BEGIN TRAN UpdateMediaTables
DECLARE @SqlVersion int
DECLARE @LogDevName nvarchar(512)
DECLARE @MediaSetId int
DECLARE @FamilySequenceNumber tinyint
DECLARE @MaxFamilySequence tinyint
DECLARE @Mirror tinyint
DECLARE @BackupFileName nvarchar(1024)
DECLARE @BackupFileNumber int
DECLARE @MediaFamilyCount int
DECLARE @MirrorCount int
DECLARE @CompressedSize numeric(20, 0)
DECLARE @sql as nvarchar(4000)
SET @SqlVersion = @@microsoftversion / 0x1000000
SET @MediaSetId = 3498233
SET @BackupFileNumber = 1
SET @MediaFamilyCount = 1
SET @MirrorCount = 1
SET @CompressedSize = 378880
SELECT @LogDevName = name
FROM master..sysdevices AS sdev
WHERE phyname = @BackupFileName
SET @sql = N'UPDATE bs SET position = @BackupFileNumber, last_family_number = @MediaFamilyCount'
IF @SqlVersion > 9 -- 2008+
BEGIN
SET @sql = @sql + N', compressed_backup_size = @CompressedSize'
END
SET @sql = @sql + N' FROM msdb..backupset AS bs
WHERE media_set_id = @MediaSetId'
EXEC sp_executesql @sql, N'@MediaFamilyCount int, @BackupFileNumber int, @CompressedSize numeric(20, 0), @MediaSetId int', @BackupFileNumber = @BackupFileNumber,@MediaFamilyCount = @MediaFamilyCount, @CompressedSize = @CompressedSize, @MediaSetId = @MediaSetId
UPDATE bms
SET media_family_count = @MediaFamilyCount, mirror_count = @MirrorCount
FROM msdb..backupmediaset AS bms
WHERE media_set_id = @MediaSetId
-- All bmf's
SET @FamilySequenceNumber = 1
SET @BackupFileName = N'\\testcase\Test_TLog_20211121180001.trn'
SET @Mirror = 0
UPDATE bmf
SET bmf.physical_device_name = @BackupFileName
FROM msdb..backupmediafamily AS bmf
WHERE bmf.media_set_id = @MediaSetId
AND bmf.family_sequence_number = @FamilySequenceNumber
AND bmf.mirror = @Mirror
UPDATE bmf
SET bmf.logical_device_name = @LogDevName
FROM msdb..backupmediafamily AS bmf
WHERE bmf.media_set_id = @MediaSetId
AND bmf.family_sequence_number = @FamilySequenceNumber
AND bmf.mirror = @Mirror
SET @MaxFamilySequence = 1
DELETE FROM msdb..backupmediafamily WITH (ROWLOCK)
WHERE media_set_id = @MediaSetId AND family_sequence_number > @MaxFamilySequence
COMMIT TRAN UpdateMediaTables
这是死锁图的 xml:
<deadlock>
<victim-list>
<victimProcess id="process2d890c8"/>
</victim-list>
<process-list>
<process id="process2d890c8" taskpriority="-5" logused="0" waitresource="PAGE: 4:1:8623 " waittime="1550" ownerId="1566457106" transactionname="UpdateMediaTables" lasttranstarted="2021-12-21T15:00:05.697" XDES="0xb16d09b80" lockMode="U" schedulerid="8" kpid="19856" status="suspended" spid="488" sbid="0" ecid="5" priority="5" trancount="0" lastbatchstarted="2021-12-21T15:00:05.660" lastbatchcompleted="2021-12-21T15:00:05.633" lastattention="1900-01-01T00:00:00.633" clientapp="Demo" hostname="AA-SQL1" hostpid="14200" isolationlevel="read uncommitted (1)" xactid="1566457106" currentdb="1" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
<executionStack>
<frame procname="adhoc" line="1" stmtstart="190" sqlhandle="0x020000000aac851a57d841f58dc63ba4bbe4b4059b68c6ab0000000000000000000000000000000000000000">
UPDATE bs SET position = @BackupFileNumber, last_family_number = @MediaFamilyCount, compressed_backup_size = @CompressedSize FROM msdb..backupset AS bs
WHERE media_set_id = @MediaSetId </frame>
<frame procname="mssqlsystemresource.sys.sp_executesql" line="1" stmtstart="-1" sqlhandle="0x0400ff7f427f99d9010000000000000000000000000000000000000000000000000000000000000000000000">
sp_executesql </frame>
<frame procname="adhoc" line="36" stmtstart="1996" stmtend="2528" sqlhandle="0x02000000a5af092360e1ef5edb55dab1b8cfba609a9b02e50000000000000000000000000000000000000000">
EXEC sp_executesql @sql, N'@MediaFamilyCount int, @BackupFileNumber int, @CompressedSize numeric(20, 0), @MediaSetId int', @BackupFileNumber = @BackupFileNumber,@MediaFamilyCount = @MediaFamilyCount, @CompressedSize = @CompressedSize, @MediaSetId = @MediaSetId </frame>
</executionStack>
<inputbuf>
BEGIN TRAN UpdateMediaTables
DECLARE @SqlVersion int
DECLARE @LogDevName nvarchar(512)
DECLARE @MediaSetId int
DECLARE @FamilySequenceNumber tinyint
DECLARE @MaxFamilySequence tinyint
DECLARE @Mirror tinyint
DECLARE @BackupFileName nvarchar(1024)
DECLARE @BackupFileNumber int
DECLARE @MediaFamilyCount int
DECLARE @MirrorCount int
DECLARE @CompressedSize numeric(20, 0)
DECLARE @sql as nvarchar(4000)
SET @SqlVersion = @@microsoftversion / 0x1000000
SET @MediaSetId = 566952
SET @BackupFileNumber = 1
SET @MediaFamilyCount = 1
SET @MirrorCount = 1
SET @CompressedSize = 156160
SELECT @LogDevName = name
FROM master..sysdevices AS sdev
WHERE phyname = @BackupFileName
SET @sql = N'UPDATE bs SET position = @BackupFileNumber, last_family_number = @MediaFamilyCount'
IF @SqlVersion > 9 -- 2008+
BEGIN
SET @sql = @sql + N', compressed_backup_size = @CompressedSize'
END
SET @sql = @sql + N' FROM msdb..backupset AS bs
WHERE media_set_id = @MediaSetId'
EXEC sp_executesql @sql, </inputbuf>
</process>
<process id="process2daa928" taskpriority="-5" logused="0" waitresource="PAGE: 4:1:9263 " waittime="1550" ownerId="1566457106" transactionname="UpdateMediaTables" lasttranstarted="2021-12-21T15:00:05.697" XDES="0x14b82acbd0" lockMode="U" schedulerid="1" kpid="8248" status="suspended" spid="488" sbid="0" ecid="6" priority="5" trancount="0" lastbatchstarted="2021-12-21T15:00:05.660" lastbatchcompleted="2021-12-21T15:00:05.633" lastattention="1900-01-01T00:00:00.633" clientapp="Demo" hostname="AA-SQL1" hostpid="14200" isolationlevel="read uncommitted (1)" xactid="1566457106" currentdb="1" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
<executionStack>
<frame procname="adhoc" line="1" stmtstart="190" sqlhandle="0x020000000aac851a57d841f58dc63ba4bbe4b4059b68c6ab0000000000000000000000000000000000000000">
UPDATE bs SET position = @BackupFileNumber, last_family_number = @MediaFamilyCount, compressed_backup_size = @CompressedSize FROM msdb..backupset AS bs
WHERE media_set_id = @MediaSetId </frame>
<frame procname="mssqlsystemresource.sys.sp_executesql" line="1" stmtstart="-1" sqlhandle="0x0400ff7f427f99d9010000000000000000000000000000000000000000000000000000000000000000000000">
sp_executesql </frame>
<frame procname="adhoc" line="36" stmtstart="1996" stmtend="2528" sqlhandle="0x02000000a5af092360e1ef5edb55dab1b8cfba609a9b02e50000000000000000000000000000000000000000">
EXEC sp_executesql @sql, N'@MediaFamilyCount int, @BackupFileNumber int, @CompressedSize numeric(20, 0), @MediaSetId int', @BackupFileNumber = @BackupFileNumber,@MediaFamilyCount = @MediaFamilyCount, @CompressedSize = @CompressedSize, @MediaSetId = @MediaSetId </frame>
</executionStack>
<inputbuf>
BEGIN TRAN UpdateMediaTables
DECLARE @SqlVersion int
DECLARE @LogDevName nvarchar(512)
DECLARE @MediaSetId int
DECLARE @FamilySequenceNumber tinyint
DECLARE @MaxFamilySequence tinyint
DECLARE @Mirror tinyint
DECLARE @BackupFileName nvarchar(1024)
DECLARE @BackupFileNumber int
DECLARE @MediaFamilyCount int
DECLARE @MirrorCount int
DECLARE @CompressedSize numeric(20, 0)
DECLARE @sql as nvarchar(4000)
SET @SqlVersion = @@microsoftversion / 0x1000000
SET @MediaSetId = 566952
SET @BackupFileNumber = 1
SET @MediaFamilyCount = 1
SET @MirrorCount = 1
SET @CompressedSize = 156160
SELECT @LogDevName = name
FROM master..sysdevices AS sdev
WHERE phyname = @BackupFileName
SET @sql = N'UPDATE bs SET position = @BackupFileNumber, last_family_number = @MediaFamilyCount'
IF @SqlVersion > 9 -- 2008+
BEGIN
SET @sql = @sql + N', compressed_backup_size = @CompressedSize'
END
SET @sql = @sql + N' FROM msdb..backupset AS bs
WHERE media_set_id = @MediaSetId'
EXEC sp_executesql @sql, </inputbuf>
</process>
<process id="process2bb4558" taskpriority="-5" logused="328" waitresource="PAGE: 4:1:9263 " waittime="948" ownerId="1566456428" transactionname="UpdateMediaTables" lasttranstarted="2021-12-21T15:00:04.410" XDES="0x14981ffb80" lockMode="U" schedulerid="7" kpid="19408" status="suspended" spid="470" sbid="0" ecid="7" priority="5" trancount="0" lastbatchstarted="2021-12-21T15:00:04.393" lastbatchcompleted="2021-12-21T15:00:04.393" lastattention="1900-01-01T00:00:00.393" clientapp="Demo" hostname="AA-SQL1" hostpid="14732" isolationlevel="read uncommitted (1)" xactid="1566456428" currentdb="1" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
<executionStack>
<frame procname="adhoc" line="1" stmtstart="190" sqlhandle="0x020000000aac851a57d841f58dc63ba4bbe4b4059b68c6ab0000000000000000000000000000000000000000">
UPDATE bs SET position = @BackupFileNumber, last_family_number = @MediaFamilyCount, compressed_backup_size = @CompressedSize FROM msdb..backupset AS bs
WHERE media_set_id = @MediaSetId </frame>
<frame procname="mssqlsystemresource.sys.sp_executesql" line="1" stmtstart="-1" sqlhandle="0x0400ff7f427f99d9010000000000000000000000000000000000000000000000000000000000000000000000">
sp_executesql </frame>
<frame procname="adhoc" line="36" stmtstart="1996" stmtend="2528" sqlhandle="0x02000000a9093d2c0a0d22ed2f73dc20e182fd1d912553b10000000000000000000000000000000000000000">
EXEC sp_executesql @sql, N'@MediaFamilyCount int, @BackupFileNumber int, @CompressedSize numeric(20, 0), @MediaSetId int', @BackupFileNumber = @BackupFileNumber,@MediaFamilyCount = @MediaFamilyCount, @CompressedSize = @CompressedSize, @MediaSetId = @MediaSetId </frame>
</executionStack>
<inputbuf>
BEGIN TRAN UpdateMediaTables
DECLARE @SqlVersion int
DECLARE @LogDevName nvarchar(512)
DECLARE @MediaSetId int
DECLARE @FamilySequenceNumber tinyint
DECLARE @MaxFamilySequence tinyint
DECLARE @Mirror tinyint
DECLARE @BackupFileName nvarchar(1024)
DECLARE @BackupFileNumber int
DECLARE @MediaFamilyCount int
DECLARE @MirrorCount int
DECLARE @CompressedSize numeric(20, 0)
DECLARE @sql as nvarchar(4000)
SET @SqlVersion = @@microsoftversion / 0x1000000
SET @MediaSetId = 566948
SET @BackupFileNumber = 1
SET @MediaFamilyCount = 1
SET @MirrorCount = 1
SET @CompressedSize = 259072
SELECT @LogDevName = name
FROM master..sysdevices AS sdev
WHERE phyname = @BackupFileName
SET @sql = N'UPDATE bs SET position = @BackupFileNumber, last_family_number = @MediaFamilyCount'
IF @SqlVersion > 9 -- 2008+
BEGIN
SET @sql = @sql + N', compressed_backup_size = @CompressedSize'
END
SET @sql = @sql + N' FROM msdb..backupset AS bs
WHERE media_set_id = @MediaSetId'
EXEC sp_executesql @sql, </inputbuf>
</process>
<process id="process2daacf8" taskpriority="-5" logused="328" waitresource="PAGE: 4:1:8623 " waittime="972" ownerId="1566456428" transactionname="UpdateMediaTables" lasttranstarted="2021-12-21T15:00:04.410" XDES="0x14b82ace80" lockMode="U" schedulerid="1" kpid="7048" status="suspended" spid="470" sbid="0" ecid="4" priority="5" trancount="0" lastbatchstarted="2021-12-21T15:00:04.393" lastbatchcompleted="2021-12-21T15:00:04.393" lastattention="1900-01-01T00:00:00.393" clientapp="Demo" hostname="AA-SQL1" hostpid="14732" isolationlevel="read uncommitted (1)" xactid="1566456428" currentdb="1" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
<executionStack>
<frame procname="adhoc" line="1" stmtstart="190" sqlhandle="0x020000000aac851a57d841f58dc63ba4bbe4b4059b68c6ab0000000000000000000000000000000000000000">
UPDATE bs SET position = @BackupFileNumber, last_family_number = @MediaFamilyCount, compressed_backup_size = @CompressedSize FROM msdb..backupset AS bs
WHERE media_set_id = @MediaSetId </frame>
<frame procname="mssqlsystemresource.sys.sp_executesql" line="1" stmtstart="-1" sqlhandle="0x0400ff7f427f99d9010000000000000000000000000000000000000000000000000000000000000000000000">
sp_executesql </frame>
<frame procname="adhoc" line="36" stmtstart="1996" stmtend="2528" sqlhandle="0x02000000a9093d2c0a0d22ed2f73dc20e182fd1d912553b10000000000000000000000000000000000000000">
EXEC sp_executesql @sql, N'@MediaFamilyCount int, @BackupFileNumber int, @CompressedSize numeric(20, 0), @MediaSetId int', @BackupFileNumber = @BackupFileNumber,@MediaFamilyCount = @MediaFamilyCount, @CompressedSize = @CompressedSize, @MediaSetId = @MediaSetId </frame>
</executionStack>
<inputbuf>
BEGIN TRAN UpdateMediaTables
DECLARE @SqlVersion int
DECLARE @LogDevName nvarchar(512)
DECLARE @MediaSetId int
DECLARE @FamilySequenceNumber tinyint
DECLARE @MaxFamilySequence tinyint
DECLARE @Mirror tinyint
DECLARE @BackupFileName nvarchar(1024)
DECLARE @BackupFileNumber int
DECLARE @MediaFamilyCount int
DECLARE @MirrorCount int
DECLARE @CompressedSize numeric(20, 0)
DECLARE @sql as nvarchar(4000)
SET @SqlVersion = @@microsoftversion / 0x1000000
SET @MediaSetId = 566948
SET @BackupFileNumber = 1
SET @MediaFamilyCount = 1
SET @MirrorCount = 1
SET @CompressedSize = 259072
SELECT @LogDevName = name
FROM master..sysdevices AS sdev
WHERE phyname = @BackupFileName
SET @sql = N'UPDATE bs SET position = @BackupFileNumber, last_family_number = @MediaFamilyCount'
IF @SqlVersion > 9 -- 2008+
BEGIN
SET @sql = @sql + N', compressed_backup_size = @CompressedSize'
END
SET @sql = @sql + N' FROM msdb..backupset AS bs
WHERE media_set_id = @MediaSetId'
EXEC sp_executesql @sql, </inputbuf>
</process>
<process id="process12ee786188" taskpriority="-5" logused="10000" waittime="730" schedulerid="12" kpid="1808" status="suspended" spid="470" sbid="0" ecid="0" priority="5" trancount="2" lastbatchstarted="2021-12-21T15:00:04.393" lastbatchcompleted="2021-12-21T15:00:04.393" lastattention="1900-01-01T00:00:00.393" clientapp="Demo" hostname="AA-SQL1" hostpid="14732" loginname="DEMO\test" isolationlevel="read uncommitted (1)" xactid="1566456428" currentdb="1" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
<executionStack>
<frame procname="adhoc" line="1" stmtstart="190" sqlhandle="0x020000000aac851a57d841f58dc63ba4bbe4b4059b68c6ab0000000000000000000000000000000000000000">
UPDATE bs SET position = @BackupFileNumber, last_family_number = @MediaFamilyCount, compressed_backup_size = @CompressedSize FROM msdb..backupset AS bs
WHERE media_set_id = @MediaSetId </frame>
<frame procname="mssqlsystemresource.sys.sp_executesql" line="1" stmtstart="-1" sqlhandle="0x0400ff7f427f99d9010000000000000000000000000000000000000000000000000000000000000000000000">
sp_executesql </frame>
<frame procname="adhoc" line="36" stmtstart="1996" stmtend="2528" sqlhandle="0x02000000a9093d2c0a0d22ed2f73dc20e182fd1d912553b10000000000000000000000000000000000000000">
EXEC sp_executesql @sql, N'@MediaFamilyCount int, @BackupFileNumber int, @CompressedSize numeric(20, 0), @MediaSetId int', @BackupFileNumber = @BackupFileNumber,@MediaFamilyCount = @MediaFamilyCount, @CompressedSize = @CompressedSize, @MediaSetId = @MediaSetId </frame>
</executionStack>
<inputbuf>
BEGIN TRAN UpdateMediaTables
DECLARE @SqlVersion int
DECLARE @LogDevName nvarchar(512)
DECLARE @MediaSetId int
DECLARE @FamilySequenceNumber tinyint
DECLARE @MaxFamilySequence tinyint
DECLARE @Mirror tinyint
DECLARE @BackupFileName nvarchar(1024)
DECLARE @BackupFileNumber int
DECLARE @MediaFamilyCount int
DECLARE @MirrorCount int
DECLARE @CompressedSize numeric(20, 0)
DECLARE @sql as nvarchar(4000)
SET @SqlVersion = @@microsoftversion / 0x1000000
SET @MediaSetId = 566948
SET @BackupFileNumber = 1
SET @MediaFamilyCount = 1
SET @MirrorCount = 1
SET @CompressedSize = 259072
SELECT @LogDevName = name
FROM master..sysdevices AS sdev
WHERE phyname = @BackupFileName
SET @sql = N'UPDATE bs SET position = @BackupFileNumber, last_family_number = @MediaFamilyCount'
IF @SqlVersion > 9 -- 2008+
BEGIN
SET @sql = @sql + N', compressed_backup_size = @CompressedSize'
END
SET @sql = @sql + N' FROM msdb..backupset AS bs
WHERE media_set_id = @MediaSetId'
EXEC sp_executesql @sql, </inputbuf>
</process>
</process-list>
<resource-list>
<pagelock fileid="1" pageid="8623" dbid="4" subresource="FULL" objectname="msdb.dbo.backupset" id="lock5e1d530780" mode="U" associatedObjectId="72057594045595648">
<owner-list>
<owner id="process12ee786188" mode="U"/>
</owner-list>
<waiter-list>
<waiter id="process2d890c8" mode="U" requestType="wait"/>
</waiter-list>
</pagelock>
<pagelock fileid="1" pageid="9263" dbid="4" subresource="FULL" objectname="msdb.dbo.backupset" id="lockf1b4c0980" mode="U" associatedObjectId="72057594045595648">
<owner-list>
<owner id="process12ee786188" mode="U"/>
</owner-list>
<waiter-list>
<waiter id="process2daa928" mode="U" requestType="wait"/>
</waiter-list>
</pagelock>
<pagelock fileid="1" pageid="9263" dbid="4" subresource="FULL" objectname="msdb.dbo.backupset" id="lockf1b4c0980" mode="U" associatedObjectId="72057594045595648">
<owner-list>
<owner id="process2daa928" mode="U" requestType="wait"/>
</owner-list>
<waiter-list>
<waiter id="process2bb4558" mode="U" requestType="wait"/>
</waiter-list>
</pagelock>
<pagelock fileid="1" pageid="8623" dbid="4" subresource="FULL" objectname="msdb.dbo.backupset" id="lock5e1d530780" mode="U" associatedObjectId="72057594045595648">
<owner-list>
<owner id="process2d890c8" mode="U" requestType="wait"/>
</owner-list>
<waiter-list>
<waiter id="process2daacf8" mode="U" requestType="wait"/>
</waiter-list>
</pagelock>
<exchangeEvent id="Pipe25c250400" WaitType="e_waitPipeGetRow" nodeId="2">
<owner-list>
<owner id="process2daacf8"/>
<owner id="process2bb4558"/>
</owner-list>
<waiter-list>
<waiter id="process12ee786188"/>
</waiter-list>
</exchangeEvent>
</resource-list>
</deadlock>
我不明白这种情况如何导致僵局。即使这两个进程并行重合,这两个事务也是相同的,因此以相同的顺序执行它们的查询。此外,更新备份集表的查询没有死锁对象,因为它是整个事务中唯一查询该表的查询。锁定是,但不是死锁。有人可以帮我理解这种死锁情况是如何发生的吗?
【问题讨论】:
猜测您可能需要在msdb..backupset (media_set_id)
上添加索引。您可能希望在该查询(动态 SQL 中的那个)上有一个 ROWLOCK
提示。旁白:动态 SQL 变量应始终声明为 nvarchar(max)
msdb..backupset (media_set_id)
已经有一个索引。我同意@Charlieface 的观点,如果你要设置行锁提示会有所帮助。发生死锁的原因是您在页面锁上存在竞争条件,其中两个进程都在另一个进程需要的页面上获取锁。您可以在包含的 XML 的底部看到这一点(请参阅<resource-list>
节点)。假设您从不并行更新相同的MediaSetId
,ROWLOCKs 应该可以解决您的问题。
【参考方案1】:
我不会诊断这个,而是点击“简单按钮”,然后使用应用程序锁序列化事务:
BEGIN TRAN UpdateMediaTables
exec sp_getapplock 'UpdateMediaTables', 'Exclusive'
. . .
那么第二个会话将在获取任何锁之前被阻塞,直到第一个会话完成。
【讨论】:
这对于这种情况非常有用。很有帮助,非常感谢以上是关于为啥相同事务的两个并行执行之间会出现死锁?的主要内容,如果未能解决你的问题,请参考以下文章