INSERT和SELECT语句之间的SQL Server死锁

时间:2010-04-29 06:38:49

标签: sql sql-server sql-server-2005 foreign-keys deadlock

我在SQL Server 2005上遇到多个死锁问题。 这个是在INSERT和SELECT语句之间。

有两张桌子。表1和表2。表2将Table1的PK(table1_id)作为外键 table1_id上的索引是聚集的。

INSERT一次在table2中插入一行 SELCET加入了2个表。 (这是一个很长的查询,可能需要12秒才能运行)

根据我的理解(和实验),INSERT应该在table1上获取一个IS锁以检查参照完整性(这不应该导致死锁)。但是,在这种情况下,它获得了一个IX页面锁定

死锁报告:

<deadlock-list>
 <deadlock victim="process968898">
  <process-list>
   <process id="process8db1f8" taskpriority="0" logused="2424" waitresource="OBJECT: 5:789577851:0 " waittime="12390" ownerId="61831512" transactionname="user_transaction" lasttranstarted="2010-04-16T07:10:13.347" XDES="0x222a8250" lockMode="IX" schedulerid="1" kpid="3764" status="suspended" spid="52" sbid="0" ecid="0" priority="0" transcount="2" lastbatchstarted="2010-04-16T07:10:13.350" lastbatchcompleted="2010-04-16T07:10:13.347" clientapp=".Net SqlClient Data Provider" hostname="VIDEV01-B-ME" hostpid="3040" loginname="DatabaseName" isolationlevel="read uncommitted (1)" xactid="61831512" currentdb="5" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
    <executionStack>
     <frame procname="DatabaseName.dbo.prcTable2_Insert" line="18" stmtstart="576" stmtend="1148" sqlhandle="0x0300050079e62d06e9307f000b9d00000100000000000000">
INSERT INTO dbo.Table2
    (
        f1,
        table1_id,
        f2
    )
    VALUES
    (
        @p1,
        @p_DocumentVersionID,
        @p1

    )     </frame>
    </executionStack>
    <inputbuf>
Proc [Database Id = 5 Object Id = 103671417]    </inputbuf>
   </process>
   <process id="process968898" taskpriority="0" logused="0" waitresource="PAGE: 5:1:46510" waittime="7625" ownerId="61831406" transactionname="INSERT" lasttranstarted="2010-04-16T07:10:12.717" XDES="0x418ec00" lockMode="S" schedulerid="2" kpid="1724" status="suspended" spid="53" sbid="0" ecid="0" priority="0" transcount="2" lastbatchstarted="2010-04-16T07:10:12.713" lastbatchcompleted="2010-04-16T07:10:12.713" clientapp=".Net SqlClient Data Provider" hostname="VIDEV01-B-ME" hostpid="3040" loginname="DatabaseName" isolationlevel="read committed (2)" xactid="61831406" currentdb="5" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
    <executionStack>
     <frame procname="DatabaseName.dbo.prcGetList" line="64" stmtstart="3548" stmtend="11570" sqlhandle="0x03000500dbcec17e8d267f000b9d00000100000000000000">
         <!-- XXXXXXXXXXXXXX...SELECT STATEMENT WITH Multiple joins including   both Table2  table 1 and .... XXXXXXXXXXXXXXX -->
    </frame>
    </executionStack>
    <inputbuf>
Proc [Database Id = 5 Object Id = 2126630619]    </inputbuf>
   </process>
  </process-list>
  <resource-list>
   <pagelock fileid="1" pageid="46510" dbid="5" objectname="DatabaseName.dbo.table1" id="lock6236bc0" mode="IX" associatedObjectId="72057594042908672">
    <owner-list>
     <owner id="process8db1f8" mode="IX"/>
    </owner-list>
    <waiter-list>
     <waiter id="process968898" mode="S" requestType="wait"/>
    </waiter-list>
   </pagelock>
   <objectlock lockPartition="0" objid="789577851" subresource="FULL" dbid="5" objectname="DatabaseName.dbo.Table2" id="lock970a240" mode="S" associatedObjectId="789577851">
    <owner-list>
     <owner id="process968898" mode="S"/>
    </owner-list>
    <waiter-list>
     <waiter id="process8db1f8" mode="IX" requestType="wait"/>
    </waiter-list>
   </objectlock>
  </resource-list>
 </deadlock>
</deadlock-list>

任何人都可以解释为什么INSERT获取IX页锁? 我没有正确阅读死锁报告吗? 顺便说一句,我还没有设法重现这个问题。

谢谢!

编辑: 表格创建:

CREATE TABLE [dbo].[Table2] (
    [Table2_id] [int] IDENTITY (1, 1) NOT NULL ,
    [f1] [int] NULL ,
    [Table1_id] [int] NOT NULL ,
    [f2] [int] NOT NULL ,
)

ALTER TABLE [dbo].[Table2] ADD 
    CONSTRAINT [FK_Table2_Table1] FOREIGN KEY 
    (
        [Table1_id]
    ) REFERENCES [dbo].[Table1] (
        [Table1_id]
    )


CREATE TABLE [dbo].[Table1] (
    [Table1_id] [int] IDENTITY (1, 1) NOT NULL ,
)

ALTER TABLE [dbo].[Table1] WITH NOCHECK ADD 
    CONSTRAINT [PK_Table1] PRIMARY KEY  CLUSTERED 
    (
        [Table1_id]
    ) 

2 个答案:

答案 0 :(得分:6)

表示“意图”锁定,并且它们始终与层次结构相关联。因为锁管理器不理解物理结构,所以他不可能尊重分层锁,因此在意图锁中重新创建层次结构。

在您的情况下,INSERT在页面上具有意图锁定。这意味着它还在页面中的一行上获得了X锁定,这是正常行为。它现在尝试获取新的IX锁,因此可能需要在不同的页面中插入一行。这将是插入到具有多个索引的表中的正常行为:第一个IX位于其中一个索引上(可能是聚簇的),第二个IX位于非聚簇索引上。

你说的SELECT在12秒内返回,因此它是一个长查询,在一个大型数据集上,并且该计划可能选择了高锁粒度,页锁。 SELECT在页面上有一个S锁定,IN​​SERT想要IX锁定,并希望在INSERT具有IX锁定的页面上有另一个S锁定。

这是一个微不足道的死锁,应该很容易修复它:确保你的SELECT不需要那些页面S锁。这不是INSERT错误。我不知道SELECt是什么,我无法确定是否是最佳的。根据我的经验,几乎总是像这样的SELECT有足够的,充足的和更多的改进空间(在SELECT本身或其下面的模式中)。

但是接受SELECT是最佳的,您最容易获得的监狱卡是row versioning

ALTER DATABASE <dbname> SET ALLOW_SNAPSHOT_ISOLATION ON;
ALTER DATABASE <dbname> SET READ_COMMITTED_SNAPSHOT ON;

更新

实际上在第二次阅读时很明显,INSERT在不同的表上有锁(除非你修改了XML,看起来在这里和那里进行了手工编辑),所以关于插入行为如何的解释必须错误。 INSERT是事务的一部分,而不是至少两次写入,一次在Table1上,一次在Table2上。但这并没有改变很多问题,也没有解决方案。确实,你有把把交易中的两个写入分成不同的交易的途径,但这显然是最糟糕的途径。

答案 1 :(得分:2)

在一个平底船,我会说DatabaseName.dbo.prcTable2_Insert正在一个事务中执行或显式打开一个,它(或与open事务的连接)已经事先插入到Table1中。