我在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]
)
答案 0 :(得分:6)
我表示“意图”锁定,并且它们始终与层次结构相关联。因为锁管理器不理解物理结构,所以他不可能尊重分层锁,因此在意图锁中重新创建层次结构。
在您的情况下,INSERT在页面上具有意图锁定。这意味着它还在页面中的一行上获得了X锁定,这是正常行为。它现在尝试获取新的IX锁,因此可能需要在不同的页面中插入一行。这将是插入到具有多个索引的表中的正常行为:第一个IX位于其中一个索引上(可能是聚簇的),第二个IX位于非聚簇索引上。
你说的SELECT在12秒内返回,因此它是一个长查询,在一个大型数据集上,并且该计划可能选择了高锁粒度,页锁。 SELECT在页面上有一个S锁定,INSERT想要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中。