我有兩個非常簡單的表,我們稱它們爲[UserData1]和[UserData2]。他們都有[UserId]列作爲主鍵。我正在對這兩個表運行兩種類型的查詢。一個是針對特定用戶,回報率數據的SELECT語句:如何從連接表中選擇時避免死鎖?
SELECT <a subset of columns from both tables>
FROM [UserData1] ud1
FULL OUTER JOIN [UserData2] ud2 ON ud1.[UserId] = ud2.[UserId]
WHERE
ud1.[UserId] = @UserId OR ud2.[UserId] = @UserId
另一種是爲特定用戶更新兩個表中的用戶數據的交易:
BEGIN TRANSACTION
UPDATE [UserData1]
SET <new values>
WHERE [UserId] = @UserId
UPDATE [UserData2]
SET <new values>
WHERE [UserId] = @UserId
COMMIT TRANSACTION
這裏的問題是在SELECT語句中獲取共享表鎖的順序未確定,如果SQL Server決定在[UserData1]之前鎖定[UserData2],則可能(並且實際上)會導致經典的死鎖情況。在這種情況下避免死鎖的最好方法是什麼?
將這些表合併到一張表中,對嗎?我希望這很容易。假設有一個理由讓他們分開。
READ UNCOMMITTED/NOLOCK提示?假設不能容忍髒讀。
SNAPSHOT隔離級別?這將解決問題,但我不確定涉及的開銷。
所以問題歸結爲:有沒有辦法保證連接表上的鎖的順序被獲取?
起初我以爲這可以通過FORCE ORDER
查詢提示來實現,但後來我通過實驗發現它並不一定強制這些表被鎖定的順序。在這種特殊情況下的另一個解決方案是爲每個表分別執行SELECT查詢,然後在應用程序層中組合兩個單行記錄集,但如果我需要爲多個用戶進行查詢,我仍然傾向於全部獲取導致一個記錄集。
UPDATE:
這是死鎖跟蹤的摘錄:
Deadlock encountered .... Printing deadlock information
Wait-for graph
Node:1
KEY: 17:72057594039173120 (e21762ccf3dc) CleanCnt:3 Mode:X Flags: 0x1
Grant List 1:
Owner:0x00000020F75B0480 Mode: X Flg:0x40 Ref:0 Life:02000000 SPID:72 ECID:0 XactLockInfo: 0x00000020EB13ED68
SPID: 72 ECID: 0 Statement Type: UPDATE Line #: 1
Input Buf: Language Event: (@UserId bigint,@DataColumn2 int)update
Requested by:
ResType:LockOwner Stype:'OR'Xdes:0x00000020FC98DA40 Mode: S SPID:75 BatchID:0 ECID:0 TaskProxy:(0x00000020DAB38608) Value:0xf75abbc0 Cost:(0/0)
Node:2
KEY: 17:72057594039107584 (e21762ccf3dc) CleanCnt:9 Mode:S Flags: 0x1
Grant List 1:
Owner:0x00000020EEBFE580 Mode: S Flg:0x40 Ref:1 Life:00000000 SPID:75 ECID:0 XactLockInfo: 0x00000020FC98DA80
SPID: 75 ECID: 0 Statement Type: SELECT Line #: 1
Input Buf: Language Event: (@UserId bigint)select [t].[UserId], t.[DataColumn2], t1.[DataColumn1]
Requested by:
ResType:LockOwner Stype:'OR'Xdes:0x00000020EB13ED28 Mode: X SPID:72 BatchID:0 ECID:0 TaskProxy:(0x0000001F671C6608) Value:0xf75b5400 Cost:(0/456)
Victim Resource Owner:
ResType:LockOwner Stype:'OR'Xdes:0x00000020FC98DA40 Mode: S SPID:75 BatchID:0 ECID:0 TaskProxy:(0x00000020DAB38608) Value:0xf75abbc0 Cost:(0/0)
deadlock-list
deadlock victim=process20fda2ccf8
process-list
process id=process20fda2ccf8 taskpriority=0 logused=0 waitresource=KEY: 17:72057594039173120 (e21762ccf3dc) waittime=4526 ownerId=3416711 transactionname=SELECT lasttranstarted=2013-07-11T18:42:20.943 XDES=0x20fc98da40 lockMode=S schedulerid=20 kpid=2800 status=suspended spid=75 sbid=0 ecid=0 priority=0 trancount=0 lastbatchstarted=2013-07-11T18:42:20.950 lastbatchcompleted=2013-07-11T18:42:20.950 lastattention=1900-01-01T00:00:00.950 clientapp=.Net SqlClient Data Provider hostname=hostname hostpid=27716 loginname=loginname isolationlevel=read committed (2) xactid=3416711 currentdb=17 lockTimeout=4294967295 clientoption1=671088672 clientoption2=128056
executionStack
frame procname=adhoc line=1 stmtstart=36 sqlhandle=0x020000001fcbbe1423a0c65cc8411344c6040e879195af3a0000000000000000000000000000000000000000
select [t].[UserId], t.[DataColumn2], t1.[DataColumn1] from [UserData1] t1 full outer join [UserData2] t on t1.[UserId]=t.[UserId] where t.[UserId][email protected] or t1.[UserId][email protected] option (force order)
frame procname=unknown line=1 sqlhandle=0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
unknown
inputbuf
(@UserId bigint)select [t].[UserId], t.[DataColumn2], t1.[DataColumn1] from [UserData1] t1 full outer join [UserData2] t on t1.[UserId]=t.[UserId] where t.[UserId][email protected] or t1.[UserId][email protected] option (force order)
process id=process20fd055498 taskpriority=0 logused=456 waitresource=KEY: 17:72057594039107584 (e21762ccf3dc) waittime=4525 ownerId=3416764 transactionname=user_transaction lasttranstarted=2013-07-11T18:42:20.960 XDES=0x20eb13ed28 lockMode=X schedulerid=9 kpid=6024 status=suspended spid=72 sbid=0 ecid=0 priority=0 trancount=2 lastbatchstarted=2013-07-11T18:42:20.970 lastbatchcompleted=2013-07-11T18:42:20.970 lastattention=1900-01-01T00:00:00.970 clientapp=.Net SqlClient Data Provider hostname=hostname hostpid=27716 loginname=loginname isolationlevel=read committed (2) xactid=3416764 currentdb=17 lockTimeout=4294967295 clientoption1=671088672 clientoption2=128056
executionStack
frame procname=adhoc line=1 stmtstart=508 sqlhandle=0x02000000c0d74a32597ec460559a2d5dbdc92f7746cdce270000000000000000000000000000000000000000
update UserData2 set [LastModified]=getutcdate(), [DataColumn2]=[DataColumn2][email protected] where [UserId][email protected]
frame procname=unknown line=1 sqlhandle=0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
unknown
inputbuf
(@UserId bigint,@DataColumn2Increment int)update UserData2 set [LastModified]=getutcdate(), [DataColumn2]=[DataColumn2][email protected] where [UserId][email protected]
resource-list
keylock hobtid=72057594039173120 dbid=17 objectname=database_name.dbo.UserData1 indexname=1 id=lock20ec75b380 mode=X associatedObjectId=72057594039173120
owner-list
owner id=process20fd055498 mode=X
waiter-list
waiter id=process20fda2ccf8 mode=S requestType=wait
keylock hobtid=72057594039107584 dbid=17 objectname=database_name.dbo.UserData2 indexname=1 id=lock20ec07f600 mode=S associatedObjectId=72057594039107584
owner-list
owner id=process20fda2ccf8 mode=S
waiter-list
waiter id=process20fd055498 mode=X requestType=wait
顯然運行SELECT語句的過程中獲得的[UserData2]之前[UserData1]表上的鎖,儘管FORCE ORDER提示。
是的,我正在陷入僵局。我用死鎖跟蹤更新了這個問題。它看起來像運行SELECT查詢的進程持有第一個鎖([UserData2]),同時嘗試獲取[UserData1]上的另一個鎖,而不是丟棄第一個鎖。 –