問題の説明
アプリケーションが同じテーブルやリソースに頻繁に読み取りや書き込みを行うと、デッドロックが発生することがあります。デッドロックが発生すると、SQL Server はトランザクションの 1 つを終了させ、次のようなエラーメッセージをクライアントに返します:
Error Message: Msg 1205, Level 13, State 47, Line 1Transaction (Process ID 53) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.
ソリューション
-
クライアントからインスタンスに接続します。詳細については、「ApsaraDB RDS for SQL Server インスタンスへの接続」をご参照ください。
-
関連するビューを監視します。
-
次の SQL ステートメントを実行して、
SYS.SYSPROCESSESを定期的に監視します。WHILE 1 = 1 BEGIN SELECT * FROM SYS.SYSPROCESSES WHERE BLOCKED <> 0; WAITFOR DELAY '[$Time]'; END;説明この例では
[$Time]を使用しています。ポーリング間隔は、00:00:01のようにカスタマイズできます。複数回のイテレーションの結果、SPID 53 は
LCK_M_Xタイプのロックを、SPID 56 はLCK_M_Sタイプのロックを待機しています。SPID 53 のblocked値は 56、SPID 56 のblocked値は 53 であり、これは互いにブロックし合っていることを示します。両方のセッションのwaittimeは、ループごとに増加します。説明監視結果では、
blocked列はブロッキングセッションのセッション ID を示し、waitresource列はブロックされているセッションが待機中のリソースを示します。この結果は、SPID 53 と SPID 56 が互いにブロックし合い、デッドロックを形成していることを示しています。 -
次の SQL ステートメントを実行して、
sys.dm_tran_locksやsys.dm_os_waiting_tasksなどのビューを定期的に監視します。while 1=1 Begin SELECT db.name DBName, tl.request_session_id, wt.blocking_session_id, OBJECT_NAME(p.OBJECT_ID) BlockedObjectName, tl.resource_type, h1.TEXT AS RequestingText, h2.TEXT AS BlockingText, tl.request_mode FROM sys.dm_tran_locks AS tl INNER JOIN sys.databases db ON db.database_id = tl.resource_database_id INNER JOIN sys.dm_os_waiting_tasks AS wt ON tl.lock_owner_address = wt.resource_address INNER JOIN sys.partitions AS p ON p.hobt_id = tl.resource_associated_entity_id INNER JOIN sys.dm_exec_connections ec1 ON ec1.session_id = tl.request_session_id INNER JOIN sys.dm_exec_connections ec2 ON ec2.session_id = wt.blocking_session_id CROSS APPLY sys.dm_exec_sql_text(ec1.most_recent_sql_handle) AS h1 CROSS APPLY sys.dm_exec_sql_text(ec2.most_recent_sql_handle) AS h2 waitfor delay '[$Time]' End2 つのセッションが互いにブロックし合い、デッドロックが発生しています。
request_session_id56 は、blocking_session_id62 によってオブジェクト Tbl2 上でブロックされており、request_modeは X です。同時に、セッション 62 はセッション 56 によってオブジェクト Tbl1 上でブロックされており、request_modeは X です。両方のセッションのRequestingTextは INSERT ステートメントを示しており、デッドロックにつながる循環依存を確認できます。返される列を次の表で説明します。
パラメーター
説明
DBNameリクエスト元のセッションがアクセスしているデータベース。
request_session_idブロックされているセッションの ID。
blocking_session_idブロッキングセッションの ID。
BlockedObjectNameブロックされているセッションがアクセス中のオブジェクト。
resource_type待機対象のリソースのタイプ。
RequestingTextブロックされているセッションが実行したステートメント。
BlockingTextブロッキングセッションが実行しているステートメント。
request_modeブロックされているセッションがリクエストしたロックモード。
-
インスタンスが ApsaraDB RDS for SQL Server 2012 を実行している場合は、SQL Server Profiler を使用してデッドロック グラフを監視し、キャプチャすることもできます。
[トレースのプロパティ] ダイアログボックスの [イベントの選択] タブで、[ロック] カテゴリを展開し、[デッドロック グラフ] イベントと [Lock:Deadlock] イベントを選択します。
キャプチャされたデッドロック グラフ:
SQL Server Profiler のトレース結果には、2 つの [Lock:Deadlock Chain] イベントが表示されます。SPID 52 は
test_shrinkデータベース (トランザクション 1374753) で 5-X (排他) ロックを保持し、一方 SPID 55 は 3-S (共有) ロック (トランザクション 1374762) を保持しています。これにより、続いて [Lock:Deadlock] イベントがトリガーされます。デッドロック グラフは、SPID 52 と SPID 55 が、それぞれtest_shrink.dbo.Th12とtest_shrink.dbo.Th11のインデックスi上のキーロックリソースをロックしていることを示しています。2 つのプロセスが互いが保持するロックをリクエストし (リクエストモードは X/S)、デッドロックとなる循環依存が発生します。
-
推奨事項
デッドロックを解決し、防止するには、次の推奨事項を参考にしてください。
-
デッドロックを迅速に解決するには、ブロッキングセッションを終了してください。
-
長時間実行トランザクションを確認し、速やかにコミットしてロックを解放してください。
-
デッドロックが共有ロックによって引き起こされ、アプリケーションがダーティリードを許容できる場合は、
WITH (NOLOCK)クエリ ヒントを使用してください。たとえば、クエリにSELECT * FROM table WITH (NOLOCK);を追加します。これにより、クエリはロックを要求せずに実行できるため、デッドロックの防止に役立ちます。 -
アプリケーションロジックを見直し、リソースに一貫した順序でアクセスするようにしてください。
関連操作
ApsaraDB RDS for SQL Server インスタンスのデッドロックの詳細は、ApsaraDB RDS コンソールで表示できます。詳細については、「デッドロック統計の表示」をご参照ください。