为什么这个触发器会导致死锁?

时间:2018-01-08 16:40:48

标签: 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]                     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

然后在我的工作站上从两个不同的SSMS实例运行这两个脚本(注意Sql实例也在我的工作站上)

BEGIN TRANSACTION
INSERT INTO T1 (ID,WHO) VALUES (0,'A')
COMMIT TRANSACTION
GO 50000

BEGIN TRANSACTION
INSERT INTO T1 (ID,WHO) VALUES (0,'B')
COMMIT TRANSACTION
GO 50000

我如何得到这样的死锁?

<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

仍遇到死锁。插入和触发器不是作为一个事务发生的,或者至少是嵌套事务吗?

1 个答案:

答案 0 :(得分:2)

您尝试在另一个线程上插入T1的同时更新T1

两个事务中的insert语句需要对表进行独占锁定并在事务持续期间保持它(i.e. TABLOCKX, HOLDLOCK),这样一旦有了锁,它们也可以自由地更新表在插入之后,没有死锁与另一个事务试图做同样的事情。