Saya bisa juga dapat mereproduksi ini 100% dari waktu di mesin saya. (lihat catatan di akhir)
Inti masalahnya adalah Anda mengeluarkan S
mengunci baris tabel sistem di tempdb
yang dapat bertentangan dengan kunci yang diperlukan untuk tempdb
internal transaksi pembersihan.
Saat pekerjaan pembersihan ini dialokasikan ke sesi yang sama yang memiliki S
mengunci, hang yang tidak terbatas dapat terjadi.
Untuk menghindari masalah ini, Anda harus berhenti mereferensikan system
objek di dalam tempdb
.
Dimungkinkan untuk membuat tabel angka tanpa merujuk tabel eksternal sama sekali. Berikut ini tidak perlu membaca baris tabel dasar dan dengan demikian juga tidak memerlukan kunci.
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
Langkah-Langkah untuk Mereproduksi
Pertama buat prosedur
CREATE PROC P
AS
SET NOCOUNT ON;
DECLARE @T TABLE (X INT)
GO
Kemudian restart Layanan SQL dan dalam satu koneksi jalankan
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
Kemudian di koneksi lain jalankan
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
Kueri yang mengisi tabel Numbers tampaknya berhasil masuk ke situasi penguncian langsung dengan transaksi sistem internal yang membersihkan objek sementara seperti variabel tabel.
Saya berhasil memblokir id sesi 53 dengan cara ini. Itu diblokir tanpa batas. Keluaran sp_WhoIsActive
menunjukkan bahwa spid ini menghabiskan hampir seluruh waktu ditangguhkan. Dalam menjalankan berturut-turut angka dalam reads
kolom meningkat tetapi nilai di kolom lain sebagian besar tetap sama.
Durasi menunggu tidak menunjukkan pola yang meningkat meskipun menunjukkan bahwa blokir harus dibuka secara berkala sebelum diblokir lagi.
SELECT *
FROM sys.dm_os_waiting_tasks
WHERE session_id = blocking_session_id
Pengembalian
+----------------------+------------+-----------------+------------------+-----------+--------------------+-----------------------+---------------------+--------------------------+--------------------------------------------------------------------------------------------------+
| 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 |
+----------------------+------------+-----------------+------------------+-----------+--------------------+-----------------------+---------------------+--------------------------+--------------------------------------------------------------------------------------------------+
Menggunakan id dalam deskripsi sumber daya
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
Pengembalian
+------------+
| name |
+------------+
| sysschobjs |
+------------+
Berlari
SELECT resource_description,request_status
FROM sys.dm_tran_locks
WHERE request_session_id = 53 AND request_status <> 'GRANT'
Pengembalian
+----------------------+----------------+
| resource_description | request_status |
+----------------------+----------------+
| (246708db8c1f) | CONVERT |
+----------------------+----------------+
Menghubungkan melalui DAC dan menjalankan
SELECT id,name
FROM tempdb.sys.sysschobjs WITH (NOLOCK)
WHERE %%LOCKRES%% = '(246708db8c1f)'
Pengembalian
+-------------+-----------+
| id | name |
+-------------+-----------+
| -1578606288 | #A1E86130 |
+-------------+-----------+
Penasaran apa itu
SELECT name,user_type_id
FROM tempdb.sys.columns
WHERE object_id = -1578606288
Pengembalian
+------+--------------+
| name | user_type_id |
+------+--------------+
| X | 56 |
+------+--------------+
Ini adalah nama kolom dalam variabel tabel yang digunakan oleh proc yang disimpan.
Berlari
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)'
Pengembalian
+--------------+----------------+--------------------+------------------+--------------------+----------------+-------------+-------------------------+
| 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 |
+--------------+----------------+--------------------+------------------+--------------------+----------------+-------------+-------------------------+
Jadi SELECT INTO
transaksi memegang S
kunci baris di tempdb.sys.sysschobjs
berkaitan dengan variabel tabel #A1E86130
. droptemp
transaksi tidak bisa mendapatkan X
kunci pada baris ini karena S
yang bertentangan ini kunci.
Menjalankan kueri ini berulang kali mengungkapkan bahwa transaction_id
untuk droptemp
transaksi berulang kali berubah.
Saya berspekulasi bahwa SQL Server harus mengalokasikan transaksi internal ini pada spid pengguna dan memprioritaskannya sebelum melakukan pekerjaan pengguna. Jadi id sesi 53 terjebak dalam siklus konstan di mana ia memulai droptemp
transaksi, diblokir oleh transaksi pengguna yang berjalan pada spid yang sama. Mengembalikan transaksi internal lalu mengulangi proses tanpa batas.
Hal ini dibuktikan dengan menelusuri berbagai penguncian dan peristiwa transaksi di SQL Server Profiler setelah spid menjadi hang.
Saya juga melacak peristiwa penguncian sebelum itu.
Kunci Pemblokiran Acara
Sebagian besar kunci kunci bersama diambil oleh SELECT INTO
transaksi pada kunci di sysschobjs
segera dibebaskan. Pengecualian adalah kunci pertama pada (246708db8c1f)
.
Ini masuk akal karena paket tersebut menunjukkan pemindaian loop bersarang dari [sys].[sysschobjs].[clst] [o]
dan karena objek sementara diberikan objek negatif, mereka akan menjadi baris pertama yang ditemukan dalam urutan pemindaian.
Saya juga mengalami situasi yang dijelaskan dalam OP di mana menjalankan gabungan silang tiga arah terlebih dahulu tampaknya memungkinkan empat arah untuk berhasil.
Beberapa peristiwa pertama dalam jejak untuk SELECT INTO
transaksi ada pola yang sama sekali berbeda.
Ini terjadi setelah layanan dimulai ulang sehingga nilai sumber daya kunci di kolom data teks tidak dapat dibandingkan secara langsung.
Alih-alih mempertahankan kunci pada kunci pertama dan kemudian pola memperoleh dan melepaskan kunci berikutnya, tampaknya memperoleh lebih banyak kunci tanpa melepaskannya pada awalnya.
Saya kira pasti ada beberapa variasi dalam strategi eksekusi yang menghindari masalah.
Perbarui
Item Hubungkan yang saya angkat tentang ini
belum ditandai sebagai diperbaiki tetapi saya sekarang menggunakan SQL Server 2012 SP2 dan sekarang hanya dapat mereproduksi pemblokiran diri sementara daripada permanen. Saya masih mendapatkan pemblokiran sendiri tetapi setelah beberapa upaya gagal untuk menjalankan droptemp
transaksi berhasil tampaknya kembali ke pemrosesan transaksi pengguna. Setelah itu melakukan transaksi sistem kemudian dijalankan dengan sukses. Masih di titik yang sama. (delapan upaya dalam satu contoh dijalankan. Saya tidak yakin apakah ini akan diulang secara konsisten)