通过在 sql 服务器中禁用 ALLOW_PAGE_LOCKS 来避免死锁

avoiding deadlock by disabling ALLOW_PAGE_LOCKS in sql server

我有一个 grails 应用程序,它公开了一个 api,它在下面有点重 DML。所有 DML 都是通过 GORM 发生的,我没有直接使用 Sql。当这个 api 同时被命中时,我 运行 经常陷入死锁,而且几乎总是主键上的这个键锁总是死锁日志的一部分。这是来自 sql 服务器的日志片段:

Date,Source,Severity,Message 09/13/2017 17:02:13,spid21s,Unknown,waiter id=process4a1fb88 mode=RangeS-U requestType=wait 09/13/2017 17:02:13,spid21s,Unknown,waiter-list 09/13/2017 17:02:13,spid21s,Unknown,owner id=process59494c8 mode=RangeS-U 09/13/2017 17:02:13,spid21s,Unknown,owner-list 09/13/2017 17:02:13,spid21s,Unknown,keylock hobtid=72057594142588928 dbid=17 objectname=EPM-DEV.dbo.ParticipantTrace indexname=PK_ParticipantTrace id=lock8d0c6c80 mode=RangeS-U associatedObjectId=72057594142588928 09/13/2017 17:02:13,spid21s,Unknown,waiter id=process59494c8 mode=RangeS-U requestType=wait 09/13/2017 17:02:13,spid21s,Unknown,waiter-list 09/13/2017 17:02:13,spid21s,Unknown,owner id=process4a1fb88 mode=RangeX-X 09/13/2017 17:02:13,spid21s,Unknown,owner-list 09/13/2017 17:02:13,spid21s,Unknown,keylock hobtid=72057594142523392 dbid=17 objectname=EPM-DEV.dbo.Critters indexname=PK_Critters id=lock909d8a00 mode=RangeX-X associatedObjectId=72057594142523392 09/13/2017 17:02:13,spid21s,Unknown,resource-list 09/13/2017 17:02:13,spid21s,Unknown,(@P0 bigint)DELETE FROM evolutions WHERE evolutionId=@P0 09/13/2017 17:02:13,spid21s,Unknown,inputbuf 09/13/2017 17:02:13,spid21s,Unknown,unknown 09/13/2017 17:02:13,spid21s,Unknown,frame procname=unknown line=1 sqlhandle=0x000000000000000000000000000000000000000000000000 09/13/2017 17:02:13,spid21s,Unknown,DELETE FROM evolutions WHERE evolutionId=@P0 09/13/2017 17:02:13,spid21s,Unknown,frame procname=adhoc line=1 stmtstart=24 sqlhandle=0x02000000791d7d1fc5ed65e7287c3344e4d309e75a370674 09/13/2017 17:02:13,spid21s,Unknown,INNER JOIN deleted ON deleted.evolutionId=evolutionRuns.evolutionId 09/13/2017 17:02:13,spid21s,Unknown,FROM EvolutionRuns 09/13/2017 17:02:13,spid21s,Unknown,DELETE FROM EvolutionRuns 09/13/2017 17:02:13,spid21s,Unknown,frame procname=EPM-DEV.dbo.tr_del_Evolutions line=7 stmtstart=314 sqlhandle=0x030011005729580f72961b01ada700000000000000000000 09/13/2017 17:02:13,spid21s,Unknown,executionStack 09/13/2017 17:02:13,spid21s,Unknown,process id=process4a1fb88 taskpriority=0 logused=278800 waitresource=KEY: 17:72057594142588928 (ffffffffffff) waittime=9495 ownerId=18259173 transactionname=implicit_transaction lasttranstarted=2017-09-13T17:02:03.317 XDES=0xe5b7d970 lockMode=RangeS-U schedulerid=1 kpid=3088 status=suspended spid=222 sbid=0 ecid=0 priority=0 trancount=2 lastbatchstarted=2017-09-13T17:02:03.580 lastbatchcompleted=2017-09-13T17:02:03.580 clientapp=PlatformService hostname=dshrestha hostpid=0 loginname=affiservUser isolationlevel=read committed (2) xactid=18259173 currentdb=17 lockTimeout=4294967295 clientoption1=671219744 clientoption2=128058 09/13/2017 17:02:13,spid21s,Unknown,(@P0 bigint)DELETE FROM evolutionRuns WHERE evolutionId=@P0 09/13/2017 17:02:13,spid21s,Unknown,inputbuf 09/13/2017 17:02:13,spid21s,Unknown,unknown 09/13/2017 17:02:13,spid21s,Unknown,frame procname=unknown line=1 sqlhandle=0x000000000000000000000000000000000000000000000000 09/13/2017 17:02:13,spid21s,Unknown,DELETE FROM evolutionRuns WHERE evolutionId=@P0 09/13/2017 17:02:13,spid21s,Unknown,frame procname=adhoc line=1 stmtstart=24 sqlhandle=0x02000000c256370374cd8f1b213e95ec8d6768101b5b0230 09/13/2017 17:02:13,spid21s,Unknown,executionStack 09/13/2017 17:02:13,spid21s,Unknown,process id=process59494c8 taskpriority=0 logused=16832 waitresource=KEY: 17:72057594142523392 (ab422a83b86b) waittime=9503 ownerId=18259172 transactionname=implicit_transaction lasttranstarted=2017-09-13T17:02:03.317 XDES=0xe5773970 lockMode=RangeS-U schedulerid=2 kpid=8652 status=suspended spid=178 sbid=0 ecid=0 priority=0 trancount=2 lastbatchstarted=2017-09-13T17:02:03.363 lastbatchcompleted=2017-09-13T17:02:03.357 clientapp=PlatformService hostname=dshrestha hostpid=0 loginname=affiservUser isolationlevel=read committed (2) xactid=18259172 currentdb=17 lockTimeout=4294967295 clientoption1=671088672 clientoption2=128058 09/13/2017 17:02:13,spid21s,Unknown,process-list 09/13/2017 17:02:13,spid21s,Unknown,deadlock victim=process59494c8 09/13/2017 17:02:13,spid21s,Unknown,deadlock-list

由于触发器以及级联删除,从 "evolution" 删除会传播删除到 "evolutionRuns" 依此类推到其他一些 table。

Table 进化触发定义如下:

ALTER TRIGGER [dbo].[tr_del_Evolutions] ON [dbo].[进化] 删除 作为 从 EvolutionRuns 中删除 来自 EvolutionRuns INNER JOIN 删除于 deleted.evolutionId=evolutionRuns.evolutionId

table ParticipantTrace 的约束定义为:

ALTER TABLE [dbo].[ParticipantTrace] WITH CHECK ADD CONSTRAINT [FK_ParticipantTrace_EvolutionRuns] FOREIGN KEY([evolutionRunId]) 参考资料 [dbo].[EvolutionRuns] ([evolutionRunId]) 删除级联

table Critters 的约束定义为:

ALTER TABLE [dbo].[Critters] WITH CHECK ADD CONSTRAINT [FK_Critters_EvolutionRuns] 外键([evolutionRunId]) 参考资料 [dbo].[EvolutionRuns] ([evolutionRunId]) 删除级联

我发现禁用 ALLOW_PAGE_LOCKS 为我解决了死锁问题,但大多数博客建议不要这样做,而是适当地添加索引。

我能解决这个键锁 + 死锁问题吗?

非常感谢您的建议,谢谢。

是的,由于级联删除,您遇到了死锁。 这是您的图表:

victim=process59494c8

has RangeS-U on PK_ParticipantTrace

waits for RangeS-U on PK_Critters

winner =process4a1fb88

has RangeX-X on keylock PK_Critters (EPM-DEV.dbo.Critters)

waits for RangeS-U on keylock PK_ParticipantTrace (EPM-DEV.dbo.ParticipantTrace)

2 个进程以相反的顺序访问这 2 tables,所以一个等待第二个 table 的锁,在第一个上持有锁,另一个在持有第一个时等待锁锁定第二个。 两个进程都认为只从 1 table 中删除,而是以不同的顺序从两者中删除