Sqlserver
 sql >> Teknologi Basis Data >  >> RDS >> Sqlserver

Mengapa kueri ini lambat pertama kali setelah saya memulai layanan?

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)



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Cara Membuat Tabel dengan Batasan Kunci Asing di SQL Server - Tutorial SQL Server / TSQL Bagian 66

  2. Log Transaksi SQL Server, Bagian 2:Arsitektur Log

  3. Bagaimana cara mengekspor data sebagai format CSV dari SQL Server menggunakan sqlcmd?

  4. Entity Framework Database.SetInitializer tidak berfungsi

  5. Database Sistem SQL Server – Pemeliharaan MSDB