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