sql >> Database >  >> RDS >> Sqlserver

Waarom is deze query de eerste keer traag nadat ik de service start?

Ik kan zou dit ook 100% van de tijd op mijn machine kunnen reproduceren. (zie opmerking aan het einde)

De kern van het probleem is dat je S . uitschakelt vergrendelt op systeemtabelrijen in tempdb die kunnen conflicteren met de sloten die nodig zijn voor interne tempdb opruimtransacties.

Wanneer dit opruimwerk wordt toegewezen aan dezelfde sessie die eigenaar is van de S lock een onbepaald vastlopen kan optreden.

Om dit probleem zeker te voorkomen, moet u stoppen met verwijzen naar het system objecten binnen tempdb .

Het is mogelijk om een ​​getallentabel te maken zonder te verwijzen naar externe tabellen. Het volgende hoeft geen basistabelrijen te lezen en neemt dus ook geen sloten.

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 

Stappen om te reproduceren

Maak eerst een procedure

CREATE PROC P
AS
    SET NOCOUNT ON;

    DECLARE @T TABLE (X INT)
GO

Start vervolgens de SQL-service opnieuw en voer in één verbinding uit

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 

Vervolgens in een andere verbinding uitvoeren

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

De query die de Numbers-tabel vult, lijkt in een live lock-situatie te komen met de interne systeemtransacties die tijdelijke objecten opruimen, zoals tabelvariabelen.

Het is me gelukt om sessie-ID 53 op deze manier te blokkeren. Het is voor onbepaalde tijd geblokkeerd. De uitvoer van sp_WhoIsActive laat zien dat deze spid bijna de hele tijd geschorst doorbrengt. In opeenvolgende runs worden de cijfers in de reads kolom neemt toe, maar de waarden in de andere kolommen blijven grotendeels hetzelfde.

De wachttijd vertoont geen toenemend patroon, maar geeft wel aan dat de blokkering periodiek moet worden opgeheven voordat deze opnieuw wordt geblokkeerd.

SELECT *
FROM   sys.dm_os_waiting_tasks
WHERE  session_id = blocking_session_id

Retourneren

+----------------------+------------+-----------------+------------------+-----------+--------------------+-----------------------+---------------------+--------------------------+--------------------------------------------------------------------------------------------------+
| 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 |
+----------------------+------------+-----------------+------------------+-----------+--------------------+-----------------------+---------------------+--------------------------+--------------------------------------------------------------------------------------------------+

Gebruik van de id in de beschrijving van de bron

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 

Retourneren

+------------+
|    name    |
+------------+
| sysschobjs |
+------------+

Hardlopen

SELECT resource_description,request_status
FROM   sys.dm_tran_locks 
WHERE request_session_id = 53 AND request_status <> 'GRANT'

Retourneren

+----------------------+----------------+
| resource_description | request_status |
+----------------------+----------------+
| (246708db8c1f)       | CONVERT        |
+----------------------+----------------+

Verbinden via de DAC en draaien

SELECT id,name
FROM   tempdb.sys.sysschobjs WITH (NOLOCK)
WHERE %%LOCKRES%% = '(246708db8c1f)' 

Retourneren

+-------------+-----------+
|     id      |   name    |
+-------------+-----------+
| -1578606288 | #A1E86130 |
+-------------+-----------+

Benieuwd wat dat is

SELECT name,user_type_id
FROM tempdb.sys.columns
WHERE object_id = -1578606288 

Retourneren

+------+--------------+
| name | user_type_id |
+------+--------------+
| X    |           56 |
+------+--------------+

Dit is de kolomnaam in de tabelvariabele die wordt gebruikt door het opgeslagen proces.

Hardlopen

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)' 

Retourneren

+--------------+----------------+--------------------+------------------+--------------------+----------------+-------------+-------------------------+
| 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 |
+--------------+----------------+--------------------+------------------+--------------------+----------------+-------------+-------------------------+

Dus de SELECT INTO transactie houdt een S vast lock op de rij in tempdb.sys.sysschobjs met betrekking tot de tabelvariabele #A1E86130 . De droptemp transactie krijgt geen X vergrendel deze rij vanwege deze conflicterende S slot.

Als u deze query herhaaldelijk uitvoert, blijkt dat de transaction_id voor de droptemp transactie verandert herhaaldelijk.

Ik speculeer dat SQL Server deze interne transacties op gebruikers-spids moet toewijzen en prioriteit moet geven voordat het gebruikerswerk wordt gedaan. Dus de sessie-ID 53 zit vast in een constante cyclus waarin het een droptemp . opstart transactie, wordt geblokkeerd door de gebruikerstransactie die op dezelfde spid draait. Draait de interne transactie terug en herhaalt het proces voor onbepaalde tijd.

Dit wordt bevestigd door de verschillende vergrendelings- en transactiegebeurtenissen in SQL Server Profiler te traceren nadat de spid is vastgelopen.

Ik heb ook de vergrendelingsgebeurtenissen daarvoor getraceerd.

Gebeurtenissen blokkeren

De meeste gedeelde sleutelvergrendelingen verwijderd door de SELECT INTO transactie op sleutels in sysschobjs onmiddellijk vrijkomen. De uitzondering is de eerste vergrendeling op (246708db8c1f) .

Dit is logisch, aangezien het plan scans van geneste lussen toont van [sys].[sysschobjs].[clst] [o] en omdat tijdelijke objecten negatieve object-ID's krijgen, zullen ze de eerste rijen zijn die in scanvolgorde worden aangetroffen.

Ik kwam ook de situatie tegen die in de OP wordt beschreven, waarbij het uitvoeren van een drieweg-kruisverbinding eerst de vierweg lijkt te laten slagen.

De eerste paar gebeurtenissen in de trace voor de SELECT INTO transactie zijn er een heel ander patroon.

Dit was na een herstart van de service, dus de vergrendelingsbronwaarden in de tekstgegevenskolom zijn niet direct vergelijkbaar.

In plaats van het slot op de eerste sleutel vast te houden en vervolgens een patroon van het verkrijgen en vrijgeven van volgende sleutels, lijkt het veel meer sloten te verwerven zonder ze in eerste instantie los te laten.

Ik neem aan dat er een variantie moet zijn in de uitvoeringsstrategie die het probleem vermijdt.

Bijwerken

Het Connect-item dat ik heb opgehaald hierover is niet gemarkeerd als vast, maar ik gebruik nu SQL Server 2012 SP2 en kan nu alleen tijdelijke zelfblokkering reproduceren in plaats van permanent. Ik krijg nog steeds de zelfblokkering, maar na een aantal mislukte pogingen om de droptemp . uit te voeren transactie succesvol blijkt terug te gaan naar het verwerken van de gebruikerstransactie. Nadat de systeemtransactie is vastgelegd, wordt deze met succes uitgevoerd. Nog steeds op dezelfde spi. (acht pogingen in één voorbeeldrun. Ik weet niet zeker of dit consequent zal worden herhaald)



  1. mysql-fout 1406

  2. Hoe Replace() werkt in SQLite

  3. SQL Server Log Verzending &Disaster Recovery Installatie en configuratie -1

  4. Langdurige 'COMMIT'-query's met de status 'inactief' in pg_stat_activity