2009-01-15 67 views
1

我正在幫助解決使用SQL Server 2005的.NET應用程序中的一些死鎖。我從下面的跟蹤中獲取XML數據。SQL Server 2005:讀取已提交事務隔離級別中的鍵範圍鎖定?

真正令我感到困惑的是當事務隔離級別被讀取提交時,PK_Exp_Experience_PriorFirm上的RangeX-X鎖定。

我讀過的所有內容都表明你只能得到你使用事務隔離級別「serializable」的鍵範圍鎖。到目前爲止,我們在我們的應用程序中找不到任何地方將隔離級別設置爲read committed以外的任何地方,下面的XML也表明我們正在使用讀取已提交。

但是,如果我們正在使用讀取提交,我不明白跟蹤顯示存在鍵範圍鎖定。有沒有人有關於如何可能發生的想法?

<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> 

回答

0

根據SQL文檔,不知何故,您的事務(或其他一個)在級別上可序列化運行。

關鍵範圍鎖定文檔here。首先一句:

鍵範圍鎖保護範圍隱含在創紀錄的行同時使用序列化事務隔離級別設置由一個Transact-SQL語句讀取。

+1

我相信這個答案是不正確。 – 2009-01-15 14:50:38

+0

Pl提供指向相關文檔的鏈接。不確定這是否添加任何相關的值作爲答案。 – Champ 2014-06-04 09:56:27

+0

根據要求添加了對TechNet文檔的鏈接。我不確定這是否是操作問題。但是,關鍵範圍鎖定通常是在可序列化隔離級別引起的,因此值得一提和調查。 – 2014-06-04 12:41:55

4

您正在使用READ COMMITED,正如您所期望的那樣。

如果UPDATE獲取聚簇索引上的排他鍵鎖定並修改了一行,並且該鎖定在羣集索引上阻止SELECT的書籤查找,則會發生類似這樣的死鎖。

這種性質的鎖通常可以通過創建一個覆蓋非聚集索引來消除。

將NOLOCK提示添加到有問題的SELECT語句是一種解決方案,前提是您可以容忍未提交的讀取。

另一個可用的選項是爲數據庫設置READ_COMMITED_SNAPSHOT ON。這改變了SELECT語句讀取提交數據的方式;他們不讀取共享鎖,而是讀取由SELECT語句開始處的事務所改變的任何數據的先前版本(快照)。儘管這不是完全免費的,在tempDB的成本是增加的活動。 [在READ COMMITED SNAPSHOT模式下,觸發器也有可能出現問題。]

2

對此的另一個常見解釋可能與IGNORE_DUP_KEY選項設置爲ON時的UNIQUE索引有關。

From BOL - 此選項指定插入操作嘗試將重複鍵值插入唯一索引時的錯誤響應。 IGNORE_DUP_KEY選項僅適用於在創建或重建索引之後插入操作。該選項在執行CREATE INDEX,ALTER INDEX或UPDATE時不起作用。默認值爲OFF。

ON 將重複的鍵值插入唯一索引時會出現警告消息。只有違反唯一性約束的行纔會失敗。

OFF 將重複的鍵值插入唯一索引時將出現錯誤消息。整個INSERT操作將被回滾。

這裏他們沒有提到的是,當啓用此選項時,INSERT中執行SERIALIZABLE隔離。我個人還沒有掌握這樣做的內在要求,因爲一些插入的行可能會被丟棄,而沒有其他的東西,但它就是這樣。排隊的SQL開發團隊在這裏編鐘...

這種行爲很容易被證明;

首先創建一個新表具有典型的PK:

CREATE TABLE [dbo].[Test_RC_TIL_RangeLocks](
    [RID] [int] IDENTITY(1,1) NOT NULL, 
    [Col1] [int] NOT NULL, 
    [Col2] [int] NOT NULL, 
    [Col3] [int] NOT NULL 
) ON [PRIMARY] 

下一步,我們要添加col1和col2上UNIQUE索引與IGNORE_DUP_KEY ON:

CREATE UNIQUE NONCLUSTERED INDEX [UIX_Test_RC_TIL_RangeLocks] ON [dbo].[Test_RC_TIL_RangeLocks](
    [Col1] ASC, 
    [Col2] ASC 
)WITH (
    PAD_INDEX = OFF, 
    STATISTICS_NORECOMPUTE = OFF, 
    SORT_IN_TEMPDB = OFF, 
    IGNORE_DUP_KEY = ON, --<<**THE OFFENDER**>> 
    DROP_EXISTING = OFF, 
    ONLINE = OFF, 
    ALLOW_ROW_LOCKS = ON, 
    ALLOW_PAGE_LOCKS = ON) 
ON [PRIMARY] 

接下來,我們將增加5行,看看會發生什麼...

SET TRANSACTION ISOLATION LEVEL READ COMMITTED 
DECLARE @C int 
SELECT @C=5 
BEGIN TRANSACTION 
WHILE @C>0 
BEGIN 
    INSERT Test_RC_TIL_RangeLocks(Col1,Col2,Col3) 
    VALUES (@C,@C+1,2*@C + 100) 
    SET @[email protected] 
END 
SELECT * FROM Test_RC_TIL_RangeLocks 
EXEC sp_lock @@SPID 
COMMIT 

正如預期的那樣,我們添加了五行:

RID Col1 Col2 Col3 
1 5  6  110 
2 4  5  108 
3 3  4  106 
4 2  3  104 
5 1  2  102 

而且我們感興趣的鎖是:

sid ObjId  IndId Type Resource     Mode 
53 0    0 DB        S 
53 0    0 DB        S 
53 402100473  1 KEY (8194443284a0)    X 
53 402100473  2 KEY (550e0a2a4b96)    RangeX-X 
53 402100473  2 KEY (ffffffffffff)    RangeS-U 
53 1131151075 0 TAB       IS 
53 402100473  1 PAG 1:744      IX 
53 402100473  2 PAG 1:748      IX 
53 402100473  1 KEY (98ec012aa510)    X 
53 402100473  1 KEY (a0c936a3c965)    X 
53 402100473  2 KEY (ec04ac4bee1f)    RangeX-X 
53 402100473  0 TAB       IX 
53 402100473  2 KEY (0207a0a08e23)    RangeX-X 
53 402100473  2 KEY (7112ec63c430)    RangeX-X 
53 402100473  1 KEY (59855d342c69)    X 
53 402100473  1 KEY (61a06abd401c)    X 
53 338100245  0 TAB       IX 

啊,可怕的SERIALIZABLE鍵範圍鎖下讀提交的隔離級別上調用一個簡單的插入!

因此,當BOL說, 在執行鍵範圍鎖定之前,必須滿足以下條件:

•事務隔離級別必須設置爲SERIALIZABLE。

請記住,這並非總是如此......

p.s.作爲設計筆記,一味消除可能具有唯一行值的重複鍵通常是不好的做法。一個更好的做法是,以確保你是不是嘗試插入重複鍵的INSERT語句中的一部分......

乾杯......