私は<ストライク>できますストライク> 私のマシンでもこれを100%再現できます。 (最後の注を参照)
問題の要点は、S
を取り出していることです tempdb
のシステムテーブル行をロックします 内部のtempdb
に必要なロックと競合する可能性があります クリーンアップトランザクション。
このクリーンアップ作業がS
を所有する同じセッションに割り当てられる場合 ロックは無期限にハングする可能性があります。
この問題を確実に回避するには、system
の参照を停止する必要があります。 tempdb
内のオブジェクト 。
外部テーブルをまったく参照せずに数値テーブルを作成することは可能です。以下は、ベーステーブルの行を読み取る必要がないため、ロックも受けません。
WITH Ten(N) AS
(
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
)
SELECT TOP 1000000 IDENTITY(INT, 1, 1) Number
INTO Numbers
FROM Ten T10,
Ten T100,
Ten T1000,
Ten T10000,
Ten T100000,
Ten T1000000
複製の手順
最初にプロシージャを作成します
CREATE PROC P
AS
SET NOCOUNT ON;
DECLARE @T TABLE (X INT)
GO
次に、SQLサービスを再起動し、1つの接続で実行します
WHILE NOT EXISTS(SELECT *
FROM sys.dm_os_waiting_tasks
WHERE session_id = blocking_session_id)
BEGIN
/*This will cause the problematic droptemp transactions*/
EXEC sp_recompile 'P'
EXEC P
END;
SELECT *
FROM sys.dm_os_waiting_tasks
WHERE session_id = blocking_session_id
次に、別の接続で実行します
USE tempdb;
SELECT TOP 1000000 IDENTITY(INT, 1, 1) Number
INTO #T
FROM sys.objects s1
CROSS JOIN sys.objects s2
CROSS JOIN sys.objects s3
CROSS JOIN sys.objects s4;
DROP TABLE #T
Numbersテーブルにデータを入力するクエリは、テーブル変数などの一時オブジェクトをクリーンアップする内部システムトランザクションでライブロック状態に陥ったようです。
この方法でセッションID53をブロックすることができました。無期限にブロックされます。 sp_WhoIsActive
の出力 は、このスパイがほとんどすべての時間を一時停止に費やしていることを示しています。連続実行では、reads
の数値 列は増加しますが、他の列の値はほぼ同じままです。
待機時間は増加するパターンを示していませんが、再度ブロックされる前に定期的にブロックを解除する必要があることを示しています。
SELECT *
FROM sys.dm_os_waiting_tasks
WHERE session_id = blocking_session_id
返品
+----------------------+------------+-----------------+------------------+-----------+--------------------+-----------------------+---------------------+--------------------------+--------------------------------------------------------------------------------------------------+
| waiting_task_address | session_id | exec_context_id | wait_duration_ms | wait_type | resource_address | blocking_task_address | blocking_session_id | blocking_exec_context_id | resource_description |
+----------------------+------------+-----------------+------------------+-----------+--------------------+-----------------------+---------------------+--------------------------+--------------------------------------------------------------------------------------------------+
| 0x00000002F2C170C8 | 53 | 0 | 86 | LCK_M_X | 0x00000002F9B13040 | 0x00000002F2C170C8 | 53 | NULL | keylock hobtid=281474978938880 dbid=2 id=lock2f9ac8880 mode=U associatedObjectId=281474978938880 |
+----------------------+------------+-----------------+------------------+-----------+--------------------+-----------------------+---------------------+--------------------------+--------------------------------------------------------------------------------------------------+
リソースの説明でIDを使用する
SELECT o.name
FROM sys.allocation_units au WITH (NOLOCK)
INNER JOIN sys.partitions p WITH (NOLOCK)
ON au.container_id = p.partition_id
INNER JOIN sys.all_objects o WITH (NOLOCK)
ON o.object_id = p.object_id
WHERE allocation_unit_id = 281474978938880
返品
+------------+
| name |
+------------+
| sysschobjs |
+------------+
実行中
SELECT resource_description,request_status
FROM sys.dm_tran_locks
WHERE request_session_id = 53 AND request_status <> 'GRANT'
返品
+----------------------+----------------+
| resource_description | request_status |
+----------------------+----------------+
| (246708db8c1f) | CONVERT |
+----------------------+----------------+
DAC経由で接続して実行中
SELECT id,name
FROM tempdb.sys.sysschobjs WITH (NOLOCK)
WHERE %%LOCKRES%% = '(246708db8c1f)'
返品
+-------------+-----------+
| id | name |
+-------------+-----------+
| -1578606288 | #A1E86130 |
+-------------+-----------+
それが何であるかについて興味があります
SELECT name,user_type_id
FROM tempdb.sys.columns
WHERE object_id = -1578606288
返品
+------+--------------+
| name | user_type_id |
+------+--------------+
| X | 56 |
+------+--------------+
これは、ストアドプロシージャによって使用されるテーブル変数の列名です。
実行中
SELECT request_mode,
request_status,
request_session_id,
request_owner_id,
lock_owner_address,
t.transaction_id,
t.name,
t.transaction_begin_time
FROM sys.dm_tran_locks l
JOIN sys.dm_tran_active_transactions t
ON l.request_owner_id = t.transaction_id
WHERE resource_description = '(246708db8c1f)'
返品
+--------------+----------------+--------------------+------------------+--------------------+----------------+-------------+-------------------------+
| request_mode | request_status | request_session_id | request_owner_id | lock_owner_address | transaction_id | name | transaction_begin_time |
+--------------+----------------+--------------------+------------------+--------------------+----------------+-------------+-------------------------+
| U | GRANT | 53 | 227647 | 0x00000002F1EF6800 | 227647 | droptemp | 2013-11-24 18:36:28.267 |
| S | GRANT | 53 | 191790 | 0x00000002F9B16380 | 191790 | SELECT INTO | 2013-11-24 18:21:30.083 |
| X | CONVERT | 53 | 227647 | 0x00000002F9B12FC0 | 227647 | droptemp | 2013-11-24 18:36:28.267 |
+--------------+----------------+--------------------+------------------+--------------------+----------------+-------------+-------------------------+
したがって、SELECT INTO
トランザクションはS
を保持しています tempdb.sys.sysschobjs
の行をロックします テーブル変数#A1E86130
に関連する 。 droptemp
トランザクションはX
を取得できません この競合するS
のため、この行をロックします ロック。
このクエリを繰り返し実行すると、transaction_id
droptemp
の場合 トランザクションは繰り返し変更されます。
SQL Serverは、ユーザーの作業を行う前に、これらの内部トランザクションをユーザーspidに割り当て、優先順位を付ける必要があると推測しています。したがって、セッションID 53は、droptemp
を起動する一定のサイクルでスタックします。 トランザクションは、同じspidで実行されているユーザートランザクションによってブロックされます。内部トランザクションをロールバックしてから、プロセスを無期限に繰り返します。
これは、spidがハングした後、SQLServerProfilerのさまざまなロックイベントとトランザクションイベントをトレースすることで裏付けられます。
また、その前にロックイベントを追跡しました。
ロックイベントのブロック
SELECT INTO
によって取得された共有キーロックのほとんど sysschobjs
のキーのトランザクション すぐに解放されます。例外は、(246708db8c1f)
の最初のロックです。 。
計画では、[sys].[sysschobjs].[clst] [o]
のネストされたループスキャンが示されているため、これはある程度意味があります。 一時オブジェクトには負のオブジェクトIDが与えられるため、スキャン順に最初に検出される行になります。
また、最初に3ウェイクロスジョインを実行すると、4ウェイクロスジョインが成功するように見えるというOPで説明されている状況に遭遇しました。
SELECT INTO
のトレースの最初のいくつかのイベント トランザクションにはまったく異なるパターンがあります。
これはサービスの再起動後だったため、テキストデータ列のロックリソース値を直接比較することはできません。
最初のキーのロックを保持してから、後続のキーを取得して解放するパターンを保持する代わりに、最初にそれらを解放せずに、より多くのロックを取得するようです。
この問題を回避するために、実行戦略にはある程度の差異があるはずです。
更新
私が提起した接続アイテムこれについて
修正済みとしてマークされていませんが、現在SQL Server 2012 SP2を使用しており、永続的ではなく一時的な自己ブロックのみを再現できるようになりました。まだセルフブロッキングが発生しますが、droptemp
の実行に何度か失敗した後 トランザクションが正常に実行されると、ユーザートランザクションの処理に戻ったように見えます。その後、システムトランザクションがコミットされ、正常に実行されます。まだ同じスパイで。 (1つの例で8回の試行が実行されます。これが一貫して繰り返されるかどうかはわかりません)