views:

1453

answers:

3

I'm helping troubleshoot some deadlocking in a .NET application that uses SQL Server 2005. I have the XML data from the trace below.

What really puzzles me is the RangeX-X lock on PK_Exp_Experience_PriorFirm when the transaction isolation level is read committed.

Everything I've read indicates that you only get a key-range lock of you are using the transaction isolation level "serializable". So far, I can't find any places in our application where we set the isolation level to anything other than read committed, and the XML below also indicates that we're using read committed.

But if we're using read-committed, I don't understand how the trace shows there being a key-range lock. Does anyone have ideas about how that could be happening?

<deadlock-list>
  <deadlock victim="processc2f438">
    <process-list>
      <process id="processc2f438" taskpriority="0" logused="13488" waitresource="KEY: 120:72057594583646208 (8201498b6efe)" waittime="484" ownerId="693258089" transactionname="user_transaction" lasttranstarted="2009-01-06T16:33:27.817" XDES="0xa71ce370" lockMode="U" schedulerid="1" kpid="9112" status="suspended" spid="53" sbid="0" ecid="0" priority="0" transcount="2" lastbatchstarted="2009-01-06T16:33:27.863" lastbatchcompleted="2009-01-06T16:33:27.863" clientapp=".Net SqlClient Data Provider" hostname="CHQAPT3" hostpid="6464" loginname="AppUser" isolationlevel="read committed (2)" xactid="693258089" currentdb="120" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
        <executionStack>
          <frame procname="adhoc" line="1" stmtstart="108" sqlhandle="0x0200000015d9962978fc6206b09e4c872150511b455e8923">
            UPDATE Exp_Experience_PriorFirm SET RelatedGuid = @newGuid WHERE RelatedGuid = @oldGuid
          </frame>
          <frame procname="mssqlsystemresource.sys.sp_executesql" line="1" sqlhandle="0x0400ff7fbe80662601000000000000000000000000000000">
            sp_executesql
          </frame>
          <frame procname="MyDb.dbo.Contact_MergeRelationships" line="74" stmtstart="4754" stmtend="4976" sqlhandle="0x0300780036a608461ed8af00669b00000100000000000000">
            EXEC sp_executesql @sql,
            N'@oldGuid uniqueidentifier, @newGuid uniqueidentifier',
            @oldGuid, @newGuid
          </frame>
          <frame procname="MyDb.dbo.Contact_Company_MergeRelationships" line="8" stmtstart="312" sqlhandle="0x03007800b271a129c8ccaf00669b00000100000000000000">
            EXEC Contact_MergeRelationships @oldGuid, @newGuid, 'Contact_Company', @excludedTableNames
          </frame>
        </executionStack>
        <inputbuf>
          Proc [Database Id = 120 Object Id = 698446258]
        </inputbuf>
      </process>
      <process id="processeb5d68" taskpriority="0" logused="14212" waitresource="KEY: 120:72057594594066432 (7c02a3a5890e)" waittime="2312" ownerId="693243114" transactionname="user_transaction" lasttranstarted="2009-01-06T16:33:20.957" XDES="0x8cdb9450" lockMode="S" schedulerid="2" kpid="9000" status="suspended" spid="73" sbid="0" ecid="0" priority="0" transcount="2" lastbatchstarted="2009-01-06T16:33:29.770" lastbatchcompleted="2009-01-06T16:33:29.770" clientapp=".Net SqlClient Data Provider" hostname="CHQAPT3" hostpid="6464" loginname="AppUser" isolationlevel="read committed (2)" xactid="693243114" currentdb="120" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
        <executionStack>
          <frame procname="MyDb.dbo.Contact_Company_Delete" line="27" stmtstart="1128" sqlhandle="0x03007800b0e5761877cbaf00669b00000100000000000000">
            DELETE FROM Contact WHERE GUID = @Guid;
          </frame>
        </executionStack>
        <inputbuf>
          Proc [Database Id = 120 Object Id = 410445232]
        </inputbuf>
      </process>
    </process-list>
    <resource-list>
      <keylock hobtid="72057594583646208" dbid="120" objectname="MyDb.dbo.Exp_Experience_PriorFirm" indexname="PK_Exp_Experience_PriorFirm" id="lockd1d43f80" mode="RangeX-X" associatedObjectId="72057594583646208">
        <owner-list>
          <owner id="processeb5d68" mode="RangeX-X"/>
        </owner-list>
        <waiter-list>
          <waiter id="processc2f438" mode="U" requestType="wait"/>
        </waiter-list>
      </keylock>
      <keylock hobtid="72057594594066432" dbid="120" objectname="MyDb.dbo.Contact_PersonCompanyLocation" indexname="PK_Contact_PersonCompanyLocation" id="lockd20c4380" mode="X" associatedObjectId="72057594594066432">
        <owner-list>
          <owner id="processc2f438" mode="X"/>
        </owner-list>
        <waiter-list>
          <waiter id="processeb5d68" mode="S" requestType="wait"/>
        </waiter-list>
      </keylock>
    </resource-list>
  </deadlock>
</deadlock-list>
A: 

According to SQL Docs, somehow, your transaction (or some other one) was running at level serializable.

Charles Bretana
I believe this answer is incorrect.
Mitch Wheat
+1  A: 

You are using READ COMMITED, as you expect.

Deadlocks like this can occur if an UPDATE acquires an eXclusive key lock on the clustered index and modifies a row, and that lock blocks a SELECT’s bookmark lookup on the clustered index.

Locks of this nature can often be eliminated by creating a covering non-clustered index.

Adding the NOLOCK hint to the offending SELECT statement is one solution provided you can tolerate un-commited reads.

Another option available to you is to set READ_COMMITED_SNAPSHOT ON for the database. This changes how the way SELECT statements read commited data; instead of taking shared locks, they read prior versions (a snapshot) of any data changed by transactions that began at the start of the SELECT statement. This doesn't come entirely for free though; the cost is increased activity in tempDB. [There is also the potential for issues with triggers in in READ COMMITED SNAPSHOT mode.]

Mitch Wheat
I agree those are all possible explanations and good suggestions for deadlocks. But I still don't understand why the RangeX-X lock is occurring if the isolation level is read committed. I'd like to understand why we have an exclusive lock on a range of keys instead of a single key.
Amy T
A: 

The RangeX-X lock on PK_Exp_Experience_PriorFirm was being taken as part of a cascading delete.

SQL Server automatically upgrades the isolation level to serializable for certain operations, such as cascading deletes.

This is described in more detail here: Conor vs. Isolation Level Upgrade on UPDATE/DELETE Cascading RI.

Amy T