Sql server 为什么这个触发器会导致死锁?
鉴于这种设置Sql server 为什么这个触发器会导致死锁?,sql-server,Sql Server,鉴于这种设置 DROP TABLE T1; CREATE TABLE T1 (RECNUM INTEGER NOT NULL IDENTITY(1,1), ID INTEGER NOT NULL, WHO VARCHAR(10) NOT NULL, DT DATETIME2 NULL DEFAULT(GETDATE()) CONSTRAINT T1_PK_ID PRIMARY KEY CLUSTERED (ID) ); GO CREATE TRIGGER [dbo].[T1_TR_AI]
DROP TABLE T1;
CREATE TABLE T1
(RECNUM INTEGER NOT NULL IDENTITY(1,1),
ID INTEGER NOT NULL,
WHO VARCHAR(10) NOT NULL,
DT DATETIME2 NULL DEFAULT(GETDATE())
CONSTRAINT T1_PK_ID PRIMARY KEY CLUSTERED (ID)
);
GO
CREATE TRIGGER [dbo].[T1_TR_AI] ON [dbo].[T1]
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
UPDATE A
SET A.ID = B.RECNUM
FROM T1 A
INNER JOIN inserted B ON (A.RECNUM = B.RECNUM)
END;
GO
然后在我的工作站上从两个不同的SSM实例运行这两个脚本(注意Sql实例也在我的工作站上)
我怎么会有这样的死锁
<deadlock>
<victim-list>
<victimProcess id="process4835828" />
</victim-list>
<process-list>
<process id="process4835828" taskpriority="0" logused="144" waitresource="KEY: 13:72057594043695104 (78d82fa561ac)" waittime="3135" ownerId="1466503" transactionname="user_transaction" lasttranstarted="2018-01-08T09:14:34.080" XDES="0x2db10f0" lockMode="X" schedulerid="5" kpid="12496" status="suspended" spid="63" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2018-01-08T09:14:34.080" lastbatchcompleted="2018-01-08T09:14:34.080" lastattention="1900-01-01T00:00:00.080" clientapp="Microsoft SQL Server Management Studio - Query" hostname="W1643558" hostpid="11600" loginname="WHQ_NT_DOMAIN\KH027556" isolationlevel="read committed (2)" xactid="1466503" currentdb="13" lockTimeout="4294967295" clientoption1="671090784" clientoption2="390200">
<executionStack>
<frame procname="adhoc" line="2" stmtstart="50" stmtend="132" sqlhandle="0x02000000f18dec12c7d493965de43d9ee9f3ec2f8011251800000000000000000000000000000000">
unknown </frame>
<frame procname="adhoc" line="2" stmtstart="38" stmtend="112" sqlhandle="0x020000001fafbb0e5b05c5ef76c1ba0a063e1165cb71b82c00000000000000000000000000000000">
unknown </frame>
</executionStack>
<inputbuf>
BEGIN TRANSACTION
INSERT INTO T1 (ID,WHO) VALUES (0,'A')
COMMIT TRANSACTION
</inputbuf>
</process>
<process id="process484d630" taskpriority="0" logused="272" waitresource="PAGE: 13:1:334 " waittime="3121" ownerId="1466501" transactionname="user_transaction" lasttranstarted="2018-01-08T09:14:34.070" XDES="0x2dbeed0" lockMode="U" schedulerid="8" kpid="19972" status="suspended" spid="62" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2018-01-08T09:14:34.070" lastbatchcompleted="2018-01-08T09:14:34.070" lastattention="1900-01-01T00:00:00.070" clientapp="Microsoft SQL Server Management Studio - Query" hostname="W1643558" hostpid="11780" loginname="WHQ_NT_DOMAIN\KH027556" isolationlevel="read committed (2)" xactid="1466501" currentdb="13" lockTimeout="4294967295" clientoption1="673319008" clientoption2="390200">
<executionStack>
<frame procname="playground.dbo.T1_TR_AI" line="6" stmtstart="316" stmtend="558" sqlhandle="0x03000d00abe7e85608a7970062a80000000000000000000000000000000000000000000000000000">
UPDATE A
SET A.ID = B.RECNUM
FROM T1 A
INNER JOIN inserted B ON (A.RECNUM = B.RECNUM </frame>
<frame procname="adhoc" line="2" stmtstart="50" stmtend="132" sqlhandle="0x02000000f18dec12c7d493965de43d9ee9f3ec2f8011251800000000000000000000000000000000">
unknown </frame>
<frame procname="adhoc" line="2" stmtstart="38" stmtend="112" sqlhandle="0x0200000018ea0a3996f68c35648ac322cee2d05b887ef67f00000000000000000000000000000000">
unknown </frame>
</executionStack>
<inputbuf>
BEGIN TRANSACTION
INSERT INTO T1 (ID,WHO) VALUES (0,'B')
COMMIT TRANSACTION
</inputbuf>
</process>
</process-list>
<resource-list>
<keylock hobtid="72057594043695104" dbid="13" objectname="playground.dbo.T1" indexname="T1_PK_ID" id="lock1da17580" mode="X" associatedObjectId="72057594043695104">
<owner-list>
<owner id="process484d630" mode="X" />
</owner-list>
<waiter-list>
<waiter id="process4835828" mode="X" requestType="wait" />
</waiter-list>
</keylock>
<pagelock fileid="1" pageid="334" dbid="13" subresource="FULL" objectname="playground.dbo.T1" id="lock20938f00" mode="IX" associatedObjectId="72057594043695104">
<owner-list>
<owner id="process4835828" mode="IX" />
</owner-list>
<waiter-list>
<waiter id="process484d630" mode="U" requestType="convert" />
</waiter-list>
</pagelock>
</resource-list>
</deadlock>
仍然遇到了僵局。插入和触发器不是作为一个事务发生的,还是至少作为一个嵌套事务发生的?您正试图在另一个线程上插入
T1
的同时更新T1
两个事务中的insert语句都需要对表进行独占锁定,并在事务期间保持它(
,即TABLOCKX,HOLDLOCK
),这样,一旦拥有了锁,它们还可以在插入后自由更新表,没有死锁,另一个事务也尝试执行相同的操作。因此第一个客户端在T1上为插入获得锁,插入后触发器是否也发生在该事务中,嵌套在原始插入事务下?@Kurt,通常错误地认为事务与锁同义。事务可以确保所有更改都成功,或者都没有成功——在死锁的情况下,没有一个成功,整个过程都会回滚。但默认情况下,事务不会锁定整个表(并且它不会自动持有整个事务的锁,除非指定SERIALIZABLE
隔离级别)。死锁基本上就是这样发生的,当两个事务各自锁定某个对象,然后各自尝试锁定另一个已经锁定的对象。我看到INSERT而非INSERT触发器出现死锁的原因如上所示,是在遇到INSERT语句时发生T1上的锁,不管实际插入发生在触发器中的事务中?@Kurt,这并不是那么简单,因为有不同类型的锁。在这种情况下,insert使用一个密钥锁来保护新值,直到提交更改为止,两个事务都可以这样做,而不会相互冲突,因此它们继续进行。但是,当他们进入下一步,即更新时,每个事务都试图获取一个页面锁(这是一个覆盖范围更广的锁)-现在两者都无法继续,因为另一方在插入的值上保留了一个冲突的密钥锁。Steve,非常感谢您提供的信息。因此,我们在高并发时间面临死锁风险。我的另一个想法是创建一个计算列,ID作为RECNUM持久化,并且仍然将ID作为我的PK。这将解决我需要ID=RECNUM的问题,但不能使用序列,因为我们支持SQL 2005。但这意味着我必须加载一个启用了IDENTITY INSERT的表副本,以将RECNUM设置为现有ID(因为它有相关的表),然后用计算列重新加载原始表。YES ID被固定到前进的标识列。我疯了吗?
<deadlock>
<victim-list>
<victimProcess id="process4835828" />
</victim-list>
<process-list>
<process id="process4835828" taskpriority="0" logused="144" waitresource="KEY: 13:72057594043695104 (78d82fa561ac)" waittime="3135" ownerId="1466503" transactionname="user_transaction" lasttranstarted="2018-01-08T09:14:34.080" XDES="0x2db10f0" lockMode="X" schedulerid="5" kpid="12496" status="suspended" spid="63" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2018-01-08T09:14:34.080" lastbatchcompleted="2018-01-08T09:14:34.080" lastattention="1900-01-01T00:00:00.080" clientapp="Microsoft SQL Server Management Studio - Query" hostname="W1643558" hostpid="11600" loginname="WHQ_NT_DOMAIN\KH027556" isolationlevel="read committed (2)" xactid="1466503" currentdb="13" lockTimeout="4294967295" clientoption1="671090784" clientoption2="390200">
<executionStack>
<frame procname="adhoc" line="2" stmtstart="50" stmtend="132" sqlhandle="0x02000000f18dec12c7d493965de43d9ee9f3ec2f8011251800000000000000000000000000000000">
unknown </frame>
<frame procname="adhoc" line="2" stmtstart="38" stmtend="112" sqlhandle="0x020000001fafbb0e5b05c5ef76c1ba0a063e1165cb71b82c00000000000000000000000000000000">
unknown </frame>
</executionStack>
<inputbuf>
BEGIN TRANSACTION
INSERT INTO T1 (ID,WHO) VALUES (0,'A')
COMMIT TRANSACTION
</inputbuf>
</process>
<process id="process484d630" taskpriority="0" logused="272" waitresource="PAGE: 13:1:334 " waittime="3121" ownerId="1466501" transactionname="user_transaction" lasttranstarted="2018-01-08T09:14:34.070" XDES="0x2dbeed0" lockMode="U" schedulerid="8" kpid="19972" status="suspended" spid="62" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2018-01-08T09:14:34.070" lastbatchcompleted="2018-01-08T09:14:34.070" lastattention="1900-01-01T00:00:00.070" clientapp="Microsoft SQL Server Management Studio - Query" hostname="W1643558" hostpid="11780" loginname="WHQ_NT_DOMAIN\KH027556" isolationlevel="read committed (2)" xactid="1466501" currentdb="13" lockTimeout="4294967295" clientoption1="673319008" clientoption2="390200">
<executionStack>
<frame procname="playground.dbo.T1_TR_AI" line="6" stmtstart="316" stmtend="558" sqlhandle="0x03000d00abe7e85608a7970062a80000000000000000000000000000000000000000000000000000">
UPDATE A
SET A.ID = B.RECNUM
FROM T1 A
INNER JOIN inserted B ON (A.RECNUM = B.RECNUM </frame>
<frame procname="adhoc" line="2" stmtstart="50" stmtend="132" sqlhandle="0x02000000f18dec12c7d493965de43d9ee9f3ec2f8011251800000000000000000000000000000000">
unknown </frame>
<frame procname="adhoc" line="2" stmtstart="38" stmtend="112" sqlhandle="0x0200000018ea0a3996f68c35648ac322cee2d05b887ef67f00000000000000000000000000000000">
unknown </frame>
</executionStack>
<inputbuf>
BEGIN TRANSACTION
INSERT INTO T1 (ID,WHO) VALUES (0,'B')
COMMIT TRANSACTION
</inputbuf>
</process>
</process-list>
<resource-list>
<keylock hobtid="72057594043695104" dbid="13" objectname="playground.dbo.T1" indexname="T1_PK_ID" id="lock1da17580" mode="X" associatedObjectId="72057594043695104">
<owner-list>
<owner id="process484d630" mode="X" />
</owner-list>
<waiter-list>
<waiter id="process4835828" mode="X" requestType="wait" />
</waiter-list>
</keylock>
<pagelock fileid="1" pageid="334" dbid="13" subresource="FULL" objectname="playground.dbo.T1" id="lock20938f00" mode="IX" associatedObjectId="72057594043695104">
<owner-list>
<owner id="process4835828" mode="IX" />
</owner-list>
<waiter-list>
<waiter id="process484d630" mode="U" requestType="convert" />
</waiter-list>
</pagelock>
</resource-list>
</deadlock>
CREATE TRIGGER [dbo].[T1_TR_II] ON [dbo].[T1]
INSTEAD OF INSERT
AS
BEGIN
BEGIN TRAN
INSERT T1
SELECT ID, WHO, DT
FROM inserted;
UPDATE T1
SET ID=RECNUM
WHERE RECNUM = @@IDENTITY
COMMIT TRAN
END