idフィールドでクラスター化インデックスと非クラスター化インデックスを使用すると、ロックの動作が異なることに気付きながら、デッドロックの問題を解決しています。clustedインデックスまたはプライマリキーがidフィールドに適用されると、デッドロックの問題は解決されるようです。
異なる行に対して1つ以上の更新を実行する異なるトランザクションがあります。たとえば、トランザクションAはID = aの行のみを更新し、tx BはID = bの行のみを更新します。
そして、インデックスなしでは、更新はすべての行の更新ロックを取得し、必要に応じて排他ロックに変換し、最終的にデッドロックにつながることを理解しています。しかし、非クラスター化インデックスでは、デッドロックが依然として存在する理由を見つけることができません(ただし、ヒット率は低下しているようです)
データ表:
CREATE TABLE [dbo].[user](
[id] [int] IDENTITY(1,1) NOT NULL,
[userName] [nvarchar](255) NULL,
[name] [nvarchar](255) NULL,
[phone] [nvarchar](255) NULL,
[password] [nvarchar](255) NULL,
[ip] [nvarchar](30) NULL,
[email] [nvarchar](255) NULL,
[pubDate] [datetime] NULL,
[todoOrder] [text] NULL
)
デッドロックトレース
deadlock-list
deadlock victim=process4152ca8
process-list
process id=process4152ca8 taskpriority=0 logused=0 waitresource=RID: 5:1:388:29 waittime=3308 ownerId=252354 transactionname=user_transaction lasttranstarted=2014-04-11T00:15:30.947 XDES=0xb0bf180 lockMode=U schedulerid=3 kpid=11392 status=suspended spid=57 sbid=0 ecid=0 priority=0 trancount=2 lastbatchstarted=2014-04-11T00:15:30.953 lastbatchcompleted=2014-04-11T00:15:30.950 lastattention=1900-01-01T00:00:00.950 clientapp=.Net SqlClient Data Provider hostname=BOOD-PC hostpid=9272 loginname=getodo_sql isolationlevel=read committed (2) xactid=252354 currentdb=5 lockTimeout=4294967295 clientoption1=671088672 clientoption2=128056
executionStack
frame procname=adhoc line=1 stmtstart=62 sqlhandle=0x0200000062f45209ccf17a0e76c2389eb409d7d970b0f89e00000000000000000000000000000000
update [user] WITH (ROWLOCK) set [todoOrder]=@para0 where id=@owner
frame procname=unknown line=1 sqlhandle=0x00000000000000000000000000000000000000000000000000000000000000000000000000000000
unknown
inputbuf
(@para0 nvarchar(2)<c/>@owner int)update [user] WITH (ROWLOCK) set [todoOrder]=@para0 where id=@owner
process id=process4153468 taskpriority=0 logused=4652 waitresource=KEY: 5:72057594042187776 (3fc56173665b) waittime=3303 ownerId=252344 transactionname=user_transaction lasttranstarted=2014-04-11T00:15:30.920 XDES=0x4184b78 lockMode=U schedulerid=3 kpid=7272 status=suspended spid=58 sbid=0 ecid=0 priority=0 trancount=2 lastbatchstarted=2014-04-11T00:15:30.960 lastbatchcompleted=2014-04-11T00:15:30.960 lastattention=1900-01-01T00:00:00.960 clientapp=.Net SqlClient Data Provider hostname=BOOD-PC hostpid=9272 loginname=getodo_sql isolationlevel=read committed (2) xactid=252344 currentdb=5 lockTimeout=4294967295 clientoption1=671088672 clientoption2=128056
executionStack
frame procname=adhoc line=1 stmtstart=60 sqlhandle=0x02000000d4616f250747930a4cd34716b610a8113cb92fbc00000000000000000000000000000000
update [user] WITH (ROWLOCK) set [todoOrder]=@para0 where id=@uid
frame procname=unknown line=1 sqlhandle=0x00000000000000000000000000000000000000000000000000000000000000000000000000000000
unknown
inputbuf
(@para0 nvarchar(61)<c/>@uid int)update [user] WITH (ROWLOCK) set [todoOrder]=@para0 where id=@uid
resource-list
ridlock fileid=1 pageid=388 dbid=5 objectname=SQL2012_707688_webows.dbo.user id=lock3f7af780 mode=X associatedObjectId=72057594042122240
owner-list
owner id=process4153468 mode=X
waiter-list
waiter id=process4152ca8 mode=U requestType=wait
keylock hobtid=72057594042187776 dbid=5 objectname=SQL2012_707688_webows.dbo.user indexname=10 id=lock3f7ad700 mode=U associatedObjectId=72057594042187776
owner-list
owner id=process4152ca8 mode=U
waiter-list
waiter id=process4153468 mode=U requestType=wait
また、興味深い可能性のある関連する発見は、クラスター化インデックスと非クラスター化インデックスが異なるロック動作を持っているように見えることです。
クラスター化インデックスを使用する場合、キーの排他ロックと、更新時にRIDの排他ロックがあります。非クラスター化インデックスが使用されている場合、2つの異なるRIDに2つの排他ロックがあり、混乱を招きます。
誰かがこれについて理由を説明できれば助かります。
テストSQL:
use SQL2012_707688_webows;
begin transaction;
update [user] with (rowlock) set todoOrder='{1}' where id = 63501
exec sp_lock;
commit;
クラスター化インデックスとしてidを使用:
spid dbid ObjId IndId Type Resource Mode Status
53 5 917578307 1 KEY (b1a92fe5eed4) X GRANT
53 5 917578307 1 PAG 1:879 IX GRANT
53 5 917578307 1 PAG 1:1928 IX GRANT
53 5 917578307 1 RID 1:879:7 X GRANT
非クラスター化インデックスとしてidを使用
spid dbid ObjId IndId Type Resource Mode Status
53 5 917578307 0 PAG 1:879 IX GRANT
53 5 917578307 0 PAG 1:1928 IX GRANT
53 5 917578307 0 RID 1:879:7 X GRANT
53 5 917578307 0 RID 1:1928:18 X GRANT
EDIT1:インデックスなしのデッドロックの詳細
2つのtx AとBがあり、それぞれに2つの更新ステートメントがあり、異なる行
tx Aがあるとします
update [user] with (rowlock) set todoOrder='{1}' where id = 63501
update [user] with (rowlock) set todoOrder='{2}' where id = 63501
tx B
update [user] with (rowlock) set todoOrder='{3}' where id = 63502
update [user] with (rowlock) set todoOrder='{4}' where id = 63502
{1}と{4}はデッドロックの可能性があります。
{1}で、テーブルスキャンを実行する必要があるため、行63502に対してUロックが要求されます。条件に一致するため、行63501でXロックが保持されている可能性があります。
{4}で、行63501に対してUロックが要求され、63502に対してXロックがすでに保持されています
txAは63501を保持し、63502を待機しますが、txBは63502を保持して63501を待機します。これはデッドロックです
EDIT2:ここで私のテストケース のバグは違いのある状況になっていることがわかりますが、混乱して申し訳ありませんが、バグは違いのある状況を作り、最終的にデッドロックを引き起こすようです。
ポールの分析がこのケースで私を本当に助けたので、私はそれを答えとして受け入れます。
私のテストケースのバグにより、2つのトランザクションtxAとtxBは、次のように同じ行を更新できます。
tx A
update [user] with (rowlock) set todoOrder='{1}' where id = 63501
update [user] with (rowlock) set todoOrder='{2}' where id = 63501
tx B
update [user] with (rowlock) set todoOrder='{3}' where id = 63501
{2}および{3}は、次の場合にデッドロックの可能性があります。
txAは、RIDのXロックを保持しながらキーのUロックを要求します({1}の更新のため)txBは、キーのUロックを保持しながらRIDのUロックを要求します