【问题标题】:Sql Server deadlock error when performing multiple Inserts within a stored procedure在存储过程中执行多个插入时出现 Sql Server 死锁错误
【发布时间】:2012-02-03 20:48:29
【问题描述】:

在 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) 不为空,
- ColumnB varchar(20) 不为空,
- 添加了DateTime 日期时间默认(getdate())

表B
- Id int identity(1,1) 主键,
- TableAId int 不为空,(FK)
- ColumnC varchar(30) 不为空

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

【问题讨论】:

  • 您是否尝试过更严格的隔离级别?
  • 顺便说一句,这似乎是过程长度与事务相结合的副作用。这是否需要事务,即您的选择使用必须未被其他可能并发操作修改的数据来创建您的插入?

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


【解决方案1】:

这种行为可能有很多不同的原因,因此您需要了解更多事实。你需要知道隔离级别和他们争夺的锁。这是我要做的:

  1. 在 Mgmt Studio 中准备好四个窗口来调用您的 proc
  2. 在每个调用者调用 proc 之前检查他们的Isolation Levels(dbcc 用户选项)。
  3. 识别四个调用者的 spid (@@spid)
  4. 在从第五个会话开始测试之前拍摄所有锁的快照:

.

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 插入后的 proc 中添加等待 (WAITFOR DELAY '00:00:30'),以便您可以查看动态。
  2. 在每个会话中开始运行 proc,但在每次启动后从您的第五个窗口拍摄锁定快照:

.

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),从而玩火。我怀疑这会导致您当前的行为,除非您的客户的超时时间非常短,但我强烈建议添加它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-07
    • 1970-01-01
    • 2021-02-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多