Sql server 存储过程导致的转换死锁
我们在一个环境中遇到了转换死锁的问题(同一个proc+触发器在至少四个其他环境中工作) 所讨论的存储过程将一行插入到一个表(cmsreceipt)中,该表具有一个更新另一个表(cmsreceiptarchive)的触发器。为了尝试使用xlock防止cmsreceiptarchive表上的select死锁,在插入之前执行rowlock,以获得触发器更新的表上的锁。这可以在四个版本的db中工作,但不能在这个环境中工作(sql 2005) 我将复制下面的死锁图,但对我来说,似乎我们在CmsReceipt表上获得的表扫描需要很长时间才能完成,这允许另一个运行相同进程的SPID也在表上获得共享锁,然后他们在准备好在CmsReceipt上进行更新后都尝试获得IX锁 我已经检查了索引(一个聚集索引和两个非聚集索引),它们与其他工作正常的数据库匹配,所以我不知道为什么我们在这个数据库上得到表扫描,而在其他数据库中没有 我尝试了各种各样的提示(在主进程和触发器中),但都没有效果 救命啊!提前感谢您的帮助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
<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]表上持有一个对象级共享锁-这很可能(没有代码就无法确定)是由以下两个因素组合而成:
编辑:添加对答案中提出的新问题的回答 好的,从你的新问题开始,我们需要做两件事:
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