在存储过程中执行多个插入时,Sql Server死锁错误

时间:2012-02-03 20:48:29

标签: sql-server biztalk deadlock sql-server-openxml

当我在sql server 2008中调用我的存储过程时,我遇到了死锁问题。一个xml字符串由biztalk传递给存储过程,并且可以快速连续多次调用。我遇到的问题是,如果程序被快速连续调用5次,则前4次调用将被回滚,最后一次调用由于死锁而被提交到数据库。下面是该过程的代码 - 它使用OPENXML解析xml字符串并插入表A.然后我从表A中获取新的唯一标识符并将多个子记录插入表B.任何有关如何解决此问题的指导都将非常感谢。

错误消息: System.Data.SqlClient.SqlException(0x80131904):事务(进程ID XX)在锁资源上与另一个进程死锁,并被选为死锁牺牲品。重新运行该交易。

表格详情
表A
- Id int identity(1,1)主键,
- ColumnA varchar(15)非null,
- ColumnB varchar(20)not null,
- AddedDateTime datetime default(getdate())

表B
- Id int identity(1,1)主键,
- TableAId int not null,(FK)
- ColumnC varchar(30)not null

XML

<Transactions>
<Transaction>
    <ColumnA>Column A Value</ColumnA>
    <ColumnB>Column B Value</ColumnB>
    <ChildItems>
        <ChildItem>
            <ColumnC>Column C Value</ColumnC>
        </ChildItem>
        <ChildItem>
            <ColumnC>Another Column C Value</ColumnC>
        </ChildItem>
        <ChildItem>
            <ColumnC>Yet Another Column C Value</ColumnC>
        </ChildItem>
    </ChildItems>



存储过程

 CREATE PROCEDURE [dbo].[proc_ProcessXml] 
    (
        @ResponseXml varchar(max)
    )
    WITH EXECUTE AS CALLER  
    AS

    BEGIN TRANSACTION
    DECLARE @xmlHandle int
    declare @tableAId int

    EXEC sp_xml_preparedocument @xmlHandle OUTPUT, @ResponseXml, 

    INSERT INTO TableA 
    (
        ColumnA,
        ColumnB             
   ) 
   SELECT columnA, columnB
    FROM OPENXML(@xmlHandle, '/Transactions/Transaction', 1)
    WITH(
        columnA varchar(15) 'ColumnA',      
        columnB varchar(20) 'ColumnB'
    )

    select @tableAId = SCOPE_IDENTITY()

    INSERT INTO TableB 
    (
        TableAId,
        ColumnC             
    )  
    SELECT @tableAId, columnC
    FROM OPENXML(@xmlHandle, '/Transactions/Transaction/ChildItems/ChildItem', 1) 
    WITH(       
        columnC varchar(30) 'ColumnC',
    )

    EXEC sp_xml_removedocument @xmlHandle

    IF @@ERROR <> 0
        BEGIN                   
            ROLLBACK
        END
    ELSE
        BEGIN           
            COMMIT
        END

1 个答案:

答案 0 :(得分:1)

这种行为可能有很多不同的原因,所以你需要获得更多的事实。您需要知道隔离级别和他们正在争取的锁定。这就是我要做的事情:

  1. 在Mgmt Studio中准备四个窗口以调用您的proc
  2. 在调用proc(dbcc useroptions)之前检查每个调用者的Isolation Levels
  3. 确定四个来电者的佣金(@@ spid)
  4. 在从第五个会话开始测试之前拍摄所有锁的快照:
  5. select
         object_name(P.object_id) as TableName, L.*
    into
        #preTestLocks
    from     
        sys.dm_tran_locks L
        join sys.partitions P on L.resource_associated_entity_id = p.hobt_id
    where
         object_name(P.object_id) in ('TableA','TableB')
    
    1. 在TableA插入(WAITFOR DELAY '00:00:30')后在proc中添加等待,以便您可以查看运动中的内容。
    2. 在每个会话中开始运行proc但在每次启动后从第五个窗口获取锁的快照:
    3. select
           object_name(P.object_id) as TableName, L.*
      into
          #lock1  --<<CHANGE AFTER EACH RUN (#lock2, #lock3 etc.)
      from     
          sys.dm_tran_locks L
          join sys.partitions P on L.resource_associated_entity_id = p.hobt_id
      where
          resource_session_id in (1,2,3,4) --<<YOUR SPID'S
      

      分析结果并查看导致死锁情况的资源。您可能会遇到锁升级问题,这会将行级锁升级到页面或扩展甚至是表级锁。 Read here for a description of Lock Modes

      最后一个观察结果:

      您可能通过在proc中启动事务并且未指定SET XACT_ABORT ON(See here for details)来玩火。我怀疑这会导致你当前的行为,除非你的客户有超短的超时,但我强烈建议你添加它。