Azure 上同一个 table 中更新的死锁与页面锁定
Deadlock on update in the same table on Azure with pagelock
我有一个 table 显示 Azure DB 上的事件。事件来得很频繁。他们在添加,然后在更新。
更新查询如下所示:
SET [DeactivationEventId] = @EventId, [Deactivated] = 1, [Type] = @DeactivationType
WHERE [DeactivationEventId] IS NULL
AND [ItemId] = @ItemId
AND [Id] <> @EventId
图形如下所示:
<victim-list>
<victimProcess id="process1752f0bbc28" />
</victim-list>
<process-list>
<process id="process1752f0bbc28" taskpriority="0" logused="888" waitresource="PAGE: 6:1:667 " waittime="4144" ownerId="7884216" transactionname="user_transaction" lasttranstarted="2020-12-14T01:39:04.073" XDES="0x1753ad74428" lockMode="S" schedulerid="2" kpid="64600" status="suspended" spid="83" sbid="2" ecid="0" priority="0" trancount="2" lastbatchstarted="2020-12-14T01:39:04.073" lastbatchcompleted="2020-12-14T01:39:04.073" lastattention="1900-01-01T00:00:00.073" clientapp="Core .Net SqlClient Data Provider" hostname="RD00155D55DC34" hostpid="8768" loginname="westbrookb" isolationlevel="read uncommitted (1)" xactid="7884216" currentdb="6" currentdbname="Rythmos" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
<executionStack>
<frame procname="unknown" queryhash="0xb5d19baa56c298af" queryplanhash="0xa0741009000fbf64" line="1" stmtstart="102" stmtend="558" sqlhandle="0x020000007676e21a17f941df30c67e9f52cb3adb4dbf4e800000000000000000000000000000000000000000">
unknown </frame>
<frame procname="unknown" queryhash="0x0000000000000000" queryplanhash="0x0000000000000000" line="1" sqlhandle="0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000">
unknown </frame>
</executionStack>
<inputbuf>
(@EventId int,@DeactivationType int,@ItemIdbigint)UPDATE [dbo].[Events] WITH (ROWLOCK)
SET [DeactivationEventId] = @EventId, [Deactivated] = 1, [Type] = @DeactivationType
WHERE [DeactivationEventId] IS NULL
AND [ItemId] = @ItemId
AND [Id] <> @EventId</inputbuf>
</process>
<process id="process17537256ca8" taskpriority="0" logused="1528" waitresource="PAGE: 6:1:667 " waittime="4141" ownerId="7884219" transactionname="user_transaction" lasttranstarted="2020-12-14T01:39:04.083" XDES="0x17541f1c428" lockMode="S" schedulerid="3" kpid="45516" status="suspended" spid="85" sbid="2" ecid="0" priority="0" trancount="2" lastbatchstarted="2020-12-14T01:39:04.087" lastbatchcompleted="2020-12-14T01:39:04.080" lastattention="1900-01-01T00:00:00.080" clientapp="Core .Net SqlClient Data Provider" hostname="RD0003FF811932" hostpid="8012" loginname="westbrookb" isolationlevel="read uncommitted (1)" xactid="7884219" currentdb="6" currentdbname="Rythmos" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
<executionStack>
<frame procname="unknown" queryhash="0xb5d19baa56c298af" queryplanhash="0xa0741009000fbf64" line="1" stmtstart="102" stmtend="558" sqlhandle="0x020000007676e21a17f941df30c67e9f52cb3adb4dbf4e800000000000000000000000000000000000000000">
unknown </frame>
<frame procname="unknown" queryhash="0x0000000000000000" queryplanhash="0x0000000000000000" line="1" sqlhandle="0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000">
unknown </frame>
</executionStack>
<inputbuf>
(@EventId int,@DeactivationType int,@ItemIdbigint)UPDATE [dbo].[Events] WITH (ROWLOCK)
SET [DeactivationEventId] = @EventId, [Deactivated] = 1, [Type] = @DeactivationType
WHERE [DeactivationEventId] IS NULL
AND [ItemId] = @ItemId
AND [Id] <> @EventId</inputbuf>
</process>
</process-list>
<resource-list>
<**pagelock** fileid="1" pageid="667" dbid="6" subresource="FULL" objectname="065e5285-8d87-4534-93bb-20d8770ae630.dbo.WirepasDeviceEvents" id="lock174acf59300" mode="IX" associatedObjectId="72057594085638144">
<owner-list>
<owner id="process17537256ca8" mode="IX" />
<owner id="process17537256ca8" mode="S" requestType="convert" />
</owner-list>
<waiter-list>
<waiter id="process1752f0bbc28" mode="S" requestType="convert" />
</waiter-list>
</pagelock>
<**pagelock** fileid="1" pageid="667" dbid="6" subresource="FULL" objectname="065e5285-8d87-4534-93bb-20d8770ae630.dbo.WirepasDeviceEvents" id="lock174acf59300" mode="IX" associatedObjectId="72057594085638144">
<owner-list>
<owner id="process1752f0bbc28" mode="IX" />
<owner id="process1752f0bbc28" mode="S" requestType="convert" />
</owner-list>
<waiter-list>
<waiter id="process17537256ca8" mode="S" requestType="convert" />
</waiter-list>
</pagelock>
</resource-list>
</deadlock>
我在此 table 上有四个非聚集索引。
这是为了避免这种僵局:
(
[DeactivationEventId] ASC,
[ItemId] ASC,
[Id] ASC
)WITH (STATISTICS_NORECOMPUTE = OFF, DROP_EXISTING = OFF, ONLINE = OFF) ON [PRIMARY]
GO
有很多警报在毫秒内插入然后更新:
**2020-12-14 01:38:00.180
2020-12-14 01:38:00.203**
我的问题。如果我用两个查询分开它:
select Id
from [dbo].[Events] WITH (NoLock)
WHERE [DeactivationEventId] IS NULL
AND [ItemId] = @ItemId
AND [Id] <> @EventId
SET [DeactivationEventId] = @EventId, [Deactivated] = 1, [Type] = @DeactivationType
WHERE Id = @Id
避免页面锁定。
这是执行计划:
你怎么看?
已编辑。
分离请求目前确实有帮助。我现在没有陷入僵局。
在图中,您可以看到死锁图的两方是相同的查询计划(具有相同的散列,因此很可能是相同的计划)。您的重写正在寻找您要更新的特定行,然后执行“广泛”更新计划来更新每个索引。它将锁定每个 b-tree/heap/etc,因为它触及每一行计划中的内容。我假设您正在使用自动提交交易,因为您没有提及任何内容。
您碰巧通过将 select 部分(可能会扫描很多行,但我无法确定计划)移出采用更新需要 U 锁。 (S 锁与其他 S 锁兼容,但与 U 锁不兼容,因此在该查询的另一个实例具有 S 锁时尝试获取 U 锁的并发查询可能会导致死锁)。
避免死锁的其他方法包括在原始的、较大的更新查询上添加 UPDLOCK 提示。对于读取的每一行,这将采用 U 锁而不是 S 锁(即使它不会被更新)。您选择的模式可能非常适合您的应用程序,但如果您在单行情况下同时执行同一查询(或针对每个尝试执行单例点查找的相同 table 的类似查询)出现死锁, 在某些情况下,不同的查询计划可以以不同的顺序为同一 table 中的索引获取锁。例如,如果查询 1 在索引 1 上锁定,然后在聚集索引上锁定,查询 2 在聚集索引上锁定,然后在索引 1 上锁定,这些也可能会死锁。可以使用 UPDLOCK 方法修复此模式。
我有一个 table 显示 Azure DB 上的事件。事件来得很频繁。他们在添加,然后在更新。
更新查询如下所示:
SET [DeactivationEventId] = @EventId, [Deactivated] = 1, [Type] = @DeactivationType
WHERE [DeactivationEventId] IS NULL
AND [ItemId] = @ItemId
AND [Id] <> @EventId
图形如下所示:
<victim-list>
<victimProcess id="process1752f0bbc28" />
</victim-list>
<process-list>
<process id="process1752f0bbc28" taskpriority="0" logused="888" waitresource="PAGE: 6:1:667 " waittime="4144" ownerId="7884216" transactionname="user_transaction" lasttranstarted="2020-12-14T01:39:04.073" XDES="0x1753ad74428" lockMode="S" schedulerid="2" kpid="64600" status="suspended" spid="83" sbid="2" ecid="0" priority="0" trancount="2" lastbatchstarted="2020-12-14T01:39:04.073" lastbatchcompleted="2020-12-14T01:39:04.073" lastattention="1900-01-01T00:00:00.073" clientapp="Core .Net SqlClient Data Provider" hostname="RD00155D55DC34" hostpid="8768" loginname="westbrookb" isolationlevel="read uncommitted (1)" xactid="7884216" currentdb="6" currentdbname="Rythmos" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
<executionStack>
<frame procname="unknown" queryhash="0xb5d19baa56c298af" queryplanhash="0xa0741009000fbf64" line="1" stmtstart="102" stmtend="558" sqlhandle="0x020000007676e21a17f941df30c67e9f52cb3adb4dbf4e800000000000000000000000000000000000000000">
unknown </frame>
<frame procname="unknown" queryhash="0x0000000000000000" queryplanhash="0x0000000000000000" line="1" sqlhandle="0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000">
unknown </frame>
</executionStack>
<inputbuf>
(@EventId int,@DeactivationType int,@ItemIdbigint)UPDATE [dbo].[Events] WITH (ROWLOCK)
SET [DeactivationEventId] = @EventId, [Deactivated] = 1, [Type] = @DeactivationType
WHERE [DeactivationEventId] IS NULL
AND [ItemId] = @ItemId
AND [Id] <> @EventId</inputbuf>
</process>
<process id="process17537256ca8" taskpriority="0" logused="1528" waitresource="PAGE: 6:1:667 " waittime="4141" ownerId="7884219" transactionname="user_transaction" lasttranstarted="2020-12-14T01:39:04.083" XDES="0x17541f1c428" lockMode="S" schedulerid="3" kpid="45516" status="suspended" spid="85" sbid="2" ecid="0" priority="0" trancount="2" lastbatchstarted="2020-12-14T01:39:04.087" lastbatchcompleted="2020-12-14T01:39:04.080" lastattention="1900-01-01T00:00:00.080" clientapp="Core .Net SqlClient Data Provider" hostname="RD0003FF811932" hostpid="8012" loginname="westbrookb" isolationlevel="read uncommitted (1)" xactid="7884219" currentdb="6" currentdbname="Rythmos" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
<executionStack>
<frame procname="unknown" queryhash="0xb5d19baa56c298af" queryplanhash="0xa0741009000fbf64" line="1" stmtstart="102" stmtend="558" sqlhandle="0x020000007676e21a17f941df30c67e9f52cb3adb4dbf4e800000000000000000000000000000000000000000">
unknown </frame>
<frame procname="unknown" queryhash="0x0000000000000000" queryplanhash="0x0000000000000000" line="1" sqlhandle="0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000">
unknown </frame>
</executionStack>
<inputbuf>
(@EventId int,@DeactivationType int,@ItemIdbigint)UPDATE [dbo].[Events] WITH (ROWLOCK)
SET [DeactivationEventId] = @EventId, [Deactivated] = 1, [Type] = @DeactivationType
WHERE [DeactivationEventId] IS NULL
AND [ItemId] = @ItemId
AND [Id] <> @EventId</inputbuf>
</process>
</process-list>
<resource-list>
<**pagelock** fileid="1" pageid="667" dbid="6" subresource="FULL" objectname="065e5285-8d87-4534-93bb-20d8770ae630.dbo.WirepasDeviceEvents" id="lock174acf59300" mode="IX" associatedObjectId="72057594085638144">
<owner-list>
<owner id="process17537256ca8" mode="IX" />
<owner id="process17537256ca8" mode="S" requestType="convert" />
</owner-list>
<waiter-list>
<waiter id="process1752f0bbc28" mode="S" requestType="convert" />
</waiter-list>
</pagelock>
<**pagelock** fileid="1" pageid="667" dbid="6" subresource="FULL" objectname="065e5285-8d87-4534-93bb-20d8770ae630.dbo.WirepasDeviceEvents" id="lock174acf59300" mode="IX" associatedObjectId="72057594085638144">
<owner-list>
<owner id="process1752f0bbc28" mode="IX" />
<owner id="process1752f0bbc28" mode="S" requestType="convert" />
</owner-list>
<waiter-list>
<waiter id="process17537256ca8" mode="S" requestType="convert" />
</waiter-list>
</pagelock>
</resource-list>
</deadlock>
我在此 table 上有四个非聚集索引。 这是为了避免这种僵局:
(
[DeactivationEventId] ASC,
[ItemId] ASC,
[Id] ASC
)WITH (STATISTICS_NORECOMPUTE = OFF, DROP_EXISTING = OFF, ONLINE = OFF) ON [PRIMARY]
GO
有很多警报在毫秒内插入然后更新: **2020-12-14 01:38:00.180
2020-12-14 01:38:00.203**
我的问题。如果我用两个查询分开它:
select Id
from [dbo].[Events] WITH (NoLock)
WHERE [DeactivationEventId] IS NULL
AND [ItemId] = @ItemId
AND [Id] <> @EventId
SET [DeactivationEventId] = @EventId, [Deactivated] = 1, [Type] = @DeactivationType
WHERE Id = @Id
避免页面锁定。
这是执行计划:
你怎么看?
已编辑。
分离请求目前确实有帮助。我现在没有陷入僵局。
在图中,您可以看到死锁图的两方是相同的查询计划(具有相同的散列,因此很可能是相同的计划)。您的重写正在寻找您要更新的特定行,然后执行“广泛”更新计划来更新每个索引。它将锁定每个 b-tree/heap/etc,因为它触及每一行计划中的内容。我假设您正在使用自动提交交易,因为您没有提及任何内容。
您碰巧通过将 select 部分(可能会扫描很多行,但我无法确定计划)移出采用更新需要 U 锁。 (S 锁与其他 S 锁兼容,但与 U 锁不兼容,因此在该查询的另一个实例具有 S 锁时尝试获取 U 锁的并发查询可能会导致死锁)。
避免死锁的其他方法包括在原始的、较大的更新查询上添加 UPDLOCK 提示。对于读取的每一行,这将采用 U 锁而不是 S 锁(即使它不会被更新)。您选择的模式可能非常适合您的应用程序,但如果您在单行情况下同时执行同一查询(或针对每个尝试执行单例点查找的相同 table 的类似查询)出现死锁, 在某些情况下,不同的查询计划可以以不同的顺序为同一 table 中的索引获取锁。例如,如果查询 1 在索引 1 上锁定,然后在聚集索引上锁定,查询 2 在聚集索引上锁定,然后在索引 1 上锁定,这些也可能会死锁。可以使用 UPDLOCK 方法修复此模式。