2015-08-24 80 views
2

SQL Server 2014 Express。SQL Server - 密鑰更新的死鎖

我我的運動簡化的問題如下:

CREATE TABLE [dbo].[foo](
    [fooid] [numeric](10, 0) IDENTITY(1,1) NOT NULL, 
    [fooval] [nvarchar](4), 
    CONSTRAINT [foo_PK] PRIMARY KEY CLUSTERED 
    (
     [fooid] ASC 
    )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
) ON [PRIMARY] 
GO 

INSERT INTO [dbo].[foo] ([fooval]) VALUES (1) 
GO 

INSERT INTO [dbo].[foo] ([fooval]) VALUES (2) 
GO 

CREATE TABLE [dbo].[bar](
    [barid] [numeric](10, 0) IDENTITY(1,1) NOT NULL, 
    [barval] [nvarchar](4), 
    CONSTRAINT [bar_PK] PRIMARY KEY CLUSTERED 
    (
     [barid] ASC 
    )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
) ON [PRIMARY] 
GO 

INSERT INTO [dbo].[bar] ([barval]) VALUES (1) 
GO 

INSERT INTO [dbo].[bar] ([barval]) VALUES (2) 
GO 

所以我必須對fooid和barid聚集主鍵兩個簡單的表。

我在兩個調試器中運行以下兩個查詢。

第一查詢:

BEGIN TRAN 
UPDATE dbo.foo SET fooval = 1 WHERE fooid = 1 

UPDATE dbo.bar SET barval = 1 WHERE barval = 1 
COMMIT 

第二查詢:

BEGIN TRAN 
UPDATE dbo.bar SET barval = 2 WHERE barid = 2 

UPDATE dbo.foo SET fooval = 2 WHERE fooval = 2 
COMMIT 

調試時,我執行查詢1的第一更新,然後查詢2的第一更新,查詢1和最後的隨後第二更新第二次更新查詢2.

這會導致死鎖。我正在運行快照隔離級別讀取已提交。

圖表顯示:

<deadlock-list> 
<deadlock victim="process2f3ed64e8"> 
    <process-list> 
    <process id="process2f3ed64e8" taskpriority="0" logused="288" waitresource="KEY: 5:72057607973896192 (227b7397de24)" waittime="2067" ownerId="1978563" transactionname="user_transaction" lasttranstarted="2015-08-24T16:24:57.280" XDES="0x2e2ff23b0" lockMode="U" schedulerid="1" kpid="9892" status="suspended" spid="59" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2015-08-24T16:24:56.997" lastbatchcompleted="2015-08-24T16:24:56.993" lastattention="1900-01-01T00:00:00.993" clientapp="Microsoft SQL Server Management Studio - Abfrage" hostname="VSL53439" hostpid="9124" loginname="x" isolationlevel="read committed (2)" xactid="1978563" currentdb="5" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056"> 
    <executionStack> 
    <frame procname="adhoc" line="6" stmtstart="38" stmtend="146" sqlhandle="0x02000000118b7210fc35334336b07155dea42e1470abe8dd0000000000000000000000000000000000000000"> 
unknown  </frame> 
    <frame procname="adhoc" line="6" stmtstart="336" stmtend="426" sqlhandle="0x02000000bf0a381fd6fec29b6ed330f87409b4e8c47d26f10000000000000000000000000000000000000000"> 
unknown  </frame> 
    </executionStack> 
    <inputbuf> 
BEGIN TRAN 
UPDATE dbo.bar SET barval = 2 WHERE barid = 2 

UPDATE dbo.foo SET fooval = 2 WHERE fooval = 2 
COMMIT </inputbuf> 
    </process> 
    <process id="process2e01b5088" taskpriority="0" logused="432" waitresource="KEY: 5:72057607973830656 (c939eba47c7b)" waittime="2970" ownerId="1978502" transactionname="user_transaction" lasttranstarted="2015-08-24T16:24:54.100" XDES="0x2df783000" lockMode="U" schedulerid="5" kpid="1928" status="suspended" spid="53" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2015-08-24T16:24:53.730" lastbatchcompleted="2015-08-24T16:24:53.730" lastattention="1900-01-01T00:00:00.730" clientapp="Microsoft SQL Server Management Studio - Abfrage" hostname="VSL53439" hostpid="4348" loginname="x" isolationlevel="read committed (2)" xactid="1978502" currentdb="5" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056"> 
    <executionStack> 
    <frame procname="adhoc" line="6" stmtstart="38" stmtend="146" sqlhandle="0x02000000f8c0c134764c79fe77f7cda514cc62eaf1a50cc80000000000000000000000000000000000000000"> 
unknown  </frame> 
    <frame procname="adhoc" line="6" stmtstart="336" stmtend="426" sqlhandle="0x020000005c75f728d068a9d6386669fb7b8e315b3e484d640000000000000000000000000000000000000000"> 
unknown  </frame> 
    </executionStack> 
    <inputbuf> 
BEGIN TRAN 
UPDATE dbo.foo SET fooval = 1 WHERE fooid = 1 

UPDATE dbo.bar SET barval = 1 WHERE barval = 1 
COMMIT </inputbuf> 
    </process> 
    </process-list> 
    <resource-list> 
    <keylock hobtid="72057607973896192" dbid="5" objectname="dbdevelop.dbo.foo" indexname="foo_PK" id="lock2ea279880" mode="X" associatedObjectId="72057607973896192"> 
    <owner-list> 
    <owner id="process2e01b5088" mode="X"/> 
    </owner-list> 
    <waiter-list> 
    <waiter id="process2f3ed64e8" mode="U" requestType="wait"/> 
    </waiter-list> 
    </keylock> 
    <keylock hobtid="72057607973830656" dbid="5" objectname="dbdevelop.dbo.bar" indexname="bar_PK" id="lock2eb0e6500" mode="X" associatedObjectId="72057607973830656"> 
    <owner-list> 
    <owner id="process2f3ed64e8" mode="X"/> 
    </owner-list> 
    <waiter-list> 
    <waiter id="process2e01b5088" mode="U" requestType="wait"/> 
    </waiter-list> 
    </keylock> 
    </resource-list> 
</deadlock> 
</deadlock-list> 

當我看看到鎖定取得我看到下面的鎖已經完成

  1. 收購 - 九 - OBJECT
  2. 收購 - IX - 收購 - X - 密鑰
  3. 收購 - X - EXTENT
  4. 釋放 - X - EXTENT
  5. 獲取 - U - EXTENT
  6. 獲取 - X - PAGE
  7. 釋放 - U - EXTENT
  8. 釋放 - X - PAGE
  9. 釋放 - 0 - KEY
  10. 發佈 - 0 - 頁

所以,一切都被釋放,除了從開始的對象,這似乎是主鍵索引。我想它會一直保存下來,直到交易完成並且不立即發佈。這似乎導致了僵局。

能否請你回答我以下的問題:

  1. 我是正確的聚集主鍵索引鎖將保留到提交?
  2. 我是否正確,這將阻止所有其他併發更新嘗試等待?
  3. 如果是這樣,爲什麼整個索引在使用where子句中的給定主鍵進行更新時會被鎖定?這意味着主鍵where子句上的每個更新都會鎖定事務的整個表。我無法相信這一點。
  4. 是在fooval和barval上添加索引的最佳解決方案?
  5. sql server與sql server express的行爲不同嗎?
+0

這是很多問題。既然你使用兩個調試器來運行單獨的事務,我假設你正在同時運行它們? –

+0

是的,如上所述: 「調試時,我執行查詢1的第一次更新,然後執行查詢2的第一次更新,然後查詢1的第二次更新,最後執行查詢2的第二次更新。」 –

+0

如果你想避免死鎖,你可以嘗試使用「while @@ Trancount <> 0,開始打印'等待'結束」,然後你的交易?這會讓它等到任何交易完成後再嘗試運行另一個交易。我不確定它是否適用 –

回答

0

穿越更新是死鎖的食譜。不管索引,索引類型等等。始終嘗試以相同的順序更新表。話雖如此,無論索引如何,如果數據位於同一頁面上,那麼您將遇到鎖定方案,並且由於您正在以縱橫交錯的方式進行更新,因此將會選擇其中一個命令作爲死鎖。

1.是
2.Yes
3,本是複雜的回答,並有在互聯網上的精彩解釋,但你應該明白的是,不管索引,鎖會發生,而且經常發生,但僵局是由於戰略不佳造成的。
4.Irrelevant
5.Yes在某些事情,但不是這種情況。