Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql server 存储过程导致的转换死锁_Sql Server_Stored Procedures_Locking_Deadlock - Fatal编程技术网

Sql server 存储过程导致的转换死锁

Sql server 存储过程导致的转换死锁,sql-server,stored-procedures,locking,deadlock,Sql Server,Stored Procedures,Locking,Deadlock,我们在一个环境中遇到了转换死锁的问题(同一个proc+触发器在至少四个其他环境中工作) 所讨论的存储过程将一行插入到一个表(cmsreceipt)中,该表具有一个更新另一个表(cmsreceiptarchive)的触发器。为了尝试使用xlock防止cmsreceiptarchive表上的select死锁,在插入之前执行rowlock,以获得触发器更新的表上的锁。这可以在四个版本的db中工作,但不能在这个环境中工作(sql 2005) 我将复制下面的死锁图,但对我来说,似乎我们在CmsReceip

我们在一个环境中遇到了转换死锁的问题(同一个proc+触发器在至少四个其他环境中工作)

所讨论的存储过程将一行插入到一个表(cmsreceipt)中,该表具有一个更新另一个表(cmsreceiptarchive)的触发器。为了尝试使用xlock防止cmsreceiptarchive表上的select死锁,在插入之前执行rowlock,以获得触发器更新的表上的锁。这可以在四个版本的db中工作,但不能在这个环境中工作(sql 2005)

我将复制下面的死锁图,但对我来说,似乎我们在CmsReceipt表上获得的表扫描需要很长时间才能完成,这允许另一个运行相同进程的SPID也在表上获得共享锁,然后他们在准备好在CmsReceipt上进行更新后都尝试获得IX锁

我已经检查了索引(一个聚集索引和两个非聚集索引),它们与其他工作正常的数据库匹配,所以我不知道为什么我们在这个数据库上得到表扫描,而在其他数据库中没有

我尝试了各种各样的提示(在主进程和触发器中),但都没有效果

救命啊!提前感谢您的帮助

<deadlock-list>
 <deadlock victim="process76d5708">
  <process-list>
<process id="process76d5708" taskpriority="0" logused="0" waitresource="OBJECT: 7:1550628567:0 " waittime="4776" ownerId="34034594" transactionguid="0x4e9e61bf45eed2429a05ad44fa09ec50" transactionname="user_transaction" lasttranstarted="2009-11-24T15:51:12.280" XDES="0x1e0ca5970" lockMode="IX" schedulerid="8" kpid="14340" status="suspended" spid="57" sbid="2" ecid="0" priority="0" trancount="3" lastbatchstarted="2009-11-24T15:51:17.513" lastbatchcompleted="2009-11-24T15:49:54.807" clientapp=".Net SqlClient Data Provider" hostname="XXX" hostpid="4804" loginname="XXXX" isolationlevel="serializable (4)" xactid="34034594" currentdb="1" lockTimeout="4294967295" clientoption1="673185824" clientoption2="128056">
<executionStack>
<frame procname="XXX.dbo.Main_InsertCmsReceipt" line="43" stmtstart="2388" stmtend="3096" sqlhandle="0x03000700d7b7b271d2daf900cb9c00000100000000000000">
insert into CmsReceipt with (updlock)  (
 CmsReceiptId,
 ModifiedAt,
 ModifiedBy,
 CmsMessageId,
 Status,
 Details,
 ReceiptTimestamp,
 SenderName,
 SenderId
    )
    values (
 @New_CmsReceiptId,
 @New_ModifiedAt,
 @New_ModifiedBy,
 @New_CmsMessageId,
 @New_Status,
 @New_Details,
 @New_ReceiptTimestamp,
 @New_SenderName,
 @New_SenderId
)     </frame>
</executionStack>
<inputbuf>

Proc [Database Id = 7 Object Id = 1907537879]    </inputbuf>
   </process>
   <process id="process70a1dc8" taskpriority="0" logused="0" waitresource="OBJECT: 7:1550628567:0 " waittime="4498" ownerId="34034604" transactionguid="0x6719e8b21f633a48bf47c77a62f2af2c" transactionname="user_transaction" lasttranstarted="2009-11-24T15:51:12.483" XDES="0x1e1a77970" lockMode="IX" schedulerid="6" kpid="13632" status="suspended" spid="69" sbid="2" ecid="0" priority="0" trancount="3" lastbatchstarted="2009-11-24T15:51:17.780" lastbatchcompleted="2009-11-24T15:49:54.807" clientapp=".Net SqlClient Data Provider" hostname="XXXX" hostpid="4804" loginname="XXXXXX" isolationlevel="serializable (4)" xactid="34034604" currentdb="1" lockTimeout="4294967295" clientoption1="673185824" clientoption2="128056">
    <executionStack>
     <frame procname="XXX.dbo.Main_InsertCmsReceipt" line="43" stmtstart="2388" stmtend="3096" sqlhandle="0x03000700d7b7b271d2daf900cb9c00000100000000000000">
insert into CmsReceipt with (updlock)  (
CmsReceiptId,
 ModifiedAt,
 ModifiedBy,
 CmsMessageId,
 Status,
 Details,
 ReceiptTimestamp,
 SenderName,
 SenderId
    )
    values (
 @New_CmsReceiptId,
 @New_ModifiedAt,
 @New_ModifiedBy,
 @New_CmsMessageId,
 @New_Status,
 @New_Details,
 @New_ReceiptTimestamp,
 @New_SenderName,
 @New_SenderId
)     </frame>
    </executionStack>
    <inputbuf>
Proc [Database Id = 7 Object Id = 1907537879]    </inputbuf>
   </process>
  </process-list>
  <resource-list>
   <objectlock lockPartition="0" objid="1550628567" subresource="FULL" dbid="7" objectname="XXX.dbo.CmsReceipt" id="lock9c4eec80" mode="S" associatedObjectId="1550628567">
    <owner-list>
     <owner id="process70a1dc8" mode="S"/>
    </owner-list>
<waiter-list>
     <waiter id="process76d5708" mode="IX" requestType="convert"/>
    </waiter-list>
   </objectlock>
   <objectlock lockPartition="0" objid="1550628567" subresource="FULL" dbid="7" objectname="XXX.dbo.CmsReceipt" id="lock9c4eec80" mode="S" associatedObjectId="1550628567">
    <owner-list>
     <owner id="process76d5708" mode="S"/>
    </owner-list>
    <waiter-list>
     <waiter id="process70a1dc8" mode="IX" requestType="convert"/>
    </waiter-list>
   </objectlock>

插入带有(updlock)的CmsReceipt(
CmsReceiptId,
修改,
由,
CmsMessageId,
地位
细节,
接收时间戳,
SenderName,
森德里德
)
价值观(
@新的CmsReceiptId,
@新修订,
@由,
@新的CmsMessageId,
@新身份,
@新的详细信息,
@新的接收时间戳,
@新森德纳姆,
@新森德里德
)     
Proc[数据库Id=7对象Id=1907537879]
插入带有(updlock)的CmsReceipt(
CmsReceiptId,
修改,
由,
CmsMessageId,
地位
细节,
接收时间戳,
SenderName,
森德里德
)
价值观(
@新的CmsReceiptId,
@新修订,
@由,
@新的CmsMessageId,
@新身份,
@新的详细信息,
@新的接收时间戳,
@新森德纳姆,
@新森德里德
)     
Proc[数据库Id=7对象Id=1907537879]

PS是否有比每行开头4个空格更简单的方法来显示xml?

首先,如果您可以发布过程代码、表模式和索引结构,那么它将非常有助于确定具体发生了什么

接下来要注意的是,您使用的是最严格的悲观锁定形式(会话的隔离级别见死锁图输出进程信息列表)。很有可能,您不需要这样做-如果您使用的是.NET TransactionScope库,我相信默认情况下它们使用Serializable,并且您希望显式指定。如果出于某种原因确实需要可序列化事务的语义,请看一看,这是一种支持序列化语义的乐观并发形式。这几乎肯定是在你们的僵局问题中扮演了一个角色,我将在下面进一步解释

至于您案例中的死锁-您在问题中提到,为了避免死锁,您使用xlock显式选择[cmsreceiptarchive]表中的rowlock,在插入[cmsreceipt]之前,插入[cmsreceipt],触发更新[cmsreceiptarchive]表的触发器(我不想在这里讨论这是否是正确的方法,因为我们看不到代码或场景,但这很可能是不必要的)。回到手头的问题—在这种情况下,[cmsreceiptarchive]表/索引没有出现死锁,[cmsreceipts]出现死锁表本身位于插入点,因此您对[cmsreceiptarchive]执行select实际上与此特定死锁无关。将死锁图解释为更简单的方法:

SPID 57 is running (line 43 of procedure XXX.dbo.Main_InsertCmsReceipt):
    insert into CmsReceipt with (updlock) ( CmsReceiptId, ModifiedAt, ModifiedBy, CmsMessageId, Status, Details, ReceiptTimestamp, SenderName, SenderId ) 
    values ( @New_CmsReceiptId, @New_ModifiedAt, @New_ModifiedBy, @New_CmsMessageId, @New_Status, @New_Details, @New_ReceiptTimestamp, @New_SenderName, @New_SenderId )

    * HOLDS a Shared lock on dbo.CmsReceipt

    * WAITING for an IX lock (convert from the S lock) on dbo.CmsReceipt
        (SPID 69 holds a conflicting Shared Object)

SPID 69 is running (line 43 of procedure XXX.dbo.Main_InsertCmsReceipt):
    insert into CmsReceipt with (updlock) ( CmsReceiptId, ModifiedAt, ModifiedBy, CmsMessageId, Status, Details, ReceiptTimestamp, SenderName, SenderId ) 
    values ( @New_CmsReceiptId, @New_ModifiedAt, @New_ModifiedBy, @New_CmsMessageId, @New_Status, @New_Details, @New_ReceiptTimestamp, @New_SenderName, @New_SenderId )

    * HOLDS a Shared lock on dbo.CmsReceipt

    * WAITING for an IX lock (convert from the S lock) on dbo.CmsReceipt
        (SPID 57 holds a conflicting Shared Object)
正如您所看到的,这里没有提到[cmsreceiptarchive]表。您有两个SPID,每个SPID在[cmsreceipt]表上持有一个对象级共享锁-这很可能(没有代码就无法确定)是由以下两个因素组合而成:

  • 您正在使用一个可序列化的隔离模型,该模型将在事务的持续时间内保持共享锁(与读取提交类型隔离模式中事务中特定语句的持续时间相反)
  • 您正在执行过程/代码的前面部分,以要求表级共享锁(可能是表扫描,或大数据块扫描,涉及足够多的行以升级到表级锁,而不是通常在可序列化事务中看到的范围锁)
  • 除非我们能看到过程中的代码,可能还有包含索引的表模式,否则这可能是我能给你的最好猜测/信息。如果你能发布过程代码、表模式和索引结构,应该能够轻松确定具体发生了什么

    至于解释死锁输出,Bart Duncan提供了一个非常推荐的读物(这也是我在这里经常使用的读物),以帮助理解/破译正在发生的事情


    编辑:添加对答案中提出的新问题的回答 好的,从你的新问题开始,我们需要做两件事:

  • DBCC USEROPTIONS不是数据库级别的上下文,而是会话(即spid、连接)级别的上下文-从中返回的内容特定于运行它的会话(在本例中,您在SSMS中的连接或从中运行它的任何内容)。READ COMMITTED很可能是默认隔离级别(除非已更改)但是,死锁场景中涉及的SPID使用的是可序列化隔离模型(如上面的输出所示)
  • 你误解了SQL_威胁在你发布的链接中所说的话——他说里面的代码
    use tempdb;
    go
    
    -- Create a test procedure to demonstrate with
    create proc usp_test
    as
    -- Set the isolation level to read uncommitted - this will be the level used
    -- for the duration of the procedure execution and any code within this procedure
    -- unless explicitly set otherwise via another set statement or query hints
    set transaction isolation level read uncommitted;
    
    -- This will show you that the isolation level is 1, which is equivalent
    -- to read uncommitted
    select  transaction_isolation_level, session_id 
    from    sys.dm_exec_sessions
    where   session_id = @@spid;
    go
    
    
    -- Now, run some code (what SQL_Menace is referring to as *inline* code)
    
    
    -- Check the current isolation level, should be the default, which is 
    -- by default READ COMMITTED (equivalent to 2)
    select  transaction_isolation_level, session_id 
    from    sys.dm_exec_sessions
    where   session_id = @@spid;
    
    -- Explicitly set the isolation level to something else, serializable. This
    -- will set the isolation method to serializable for this session and any
    -- code executed in this context, unless explicitly set to something else
    set transaction isolation level serializable;
    
    -- Take another look at the isolation level - now will be 4, serializable
    select  transaction_isolation_level, session_id 
    from    sys.dm_exec_sessions
    where   session_id = @@spid;
    
    -- Execute the stored procedure - note that within the stored procedure's
    -- context, the isolation level is running at 1 (read uncommitted)
    exec usp_test;
    
    -- Check the isolation level in this session/context again - note that it
    -- is again running under the serializable isolation level, since the
    -- read uncommitted level only applies for the duration of the procedure
    -- code context
    select  transaction_isolation_level, session_id 
    from    sys.dm_exec_sessions
    where   session_id = @@spid;
    
    -- Repeat the same tests using a different isolation level - it isn't
    -- always serializable, it is whatever the session is set to, which can
    -- be the default or whatever you explicitly set it to
    set transaction isolation level repeatable read;
    
    -- Now it is 3 (repeatable read)...
    select  transaction_isolation_level, session_id 
    from    sys.dm_exec_sessions
    where   session_id = @@spid;
    
    -- Still going to be 1 within the procedure
    exec usp_test;
    
    -- Back to 3 again (repeatable read)
    select  transaction_isolation_level, session_id 
    from    sys.dm_exec_sessions
    where   session_id = @@spid;
    
    go
    
    
    -- Cleanup
    drop procedure usp_test;
    go