sql >> Databasteknik >  >> RDS >> Sqlserver

Varför är den här frågan långsam första gången efter att jag startar tjänsten?

Jag kan kunde också återge detta 100% av tiden på min maskin. (se not i slutet)

Kärnan i problemet är att du tar ut S låser på systemtabellrader i tempdb som kan komma i konflikt med de lås som behövs för intern tempdb saneringstransaktioner.

När detta saneringsarbete allokeras till samma session som äger S lås ett obestämt häng kan inträffa.

För att undvika detta problem med säkerhet måste du sluta referera till system objekt inuti tempdb .

Det är möjligt att skapa en taltabell utan att referera till några externa tabeller alls. Följande behöver inte läsa några bastabellrader och tar därför inte heller några lås.

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 

Steg för att återskapa

Skapa först en procedur

CREATE PROC P
AS
    SET NOCOUNT ON;

    DECLARE @T TABLE (X INT)
GO

Starta sedan om SQL Service och kör i en anslutning

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 

Kör sedan i en annan anslutning

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

Frågan som fyller i Numbers-tabellen verkar lyckas hamna i en livelåssituation med de interna systemtransaktionerna som rensar upp tillfälliga objekt som tabellvariabler.

Jag lyckades få sessions-id 53 blockerad på detta sätt. Den är blockerad på obestämd tid. Utdata från sp_WhoIsActive visar att denna spindel tillbringar nästan hela tiden avstängd. I på varandra följande körningar siffrorna i reads kolumnen ökar men värdena i de andra kolumnerna förblir i stort sett desamma.

Väntetiden visar inte ett ökande mönster men indikerar att den måste avblockeras med jämna mellanrum innan den blockeras igen.

SELECT *
FROM   sys.dm_os_waiting_tasks
WHERE  session_id = blocking_session_id

Retur

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

Använda id:t i resursbeskrivningen

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 

Retur

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

Kör

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

Retur

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

Ansluter via DAC och kör

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

Retur

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

Nyfiken på vad det är

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

Retur

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

Detta är kolumnnamnet i tabellvariabeln som används av den lagrade proc.

Kör

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

Retur

+--------------+----------------+--------------------+------------------+--------------------+----------------+-------------+-------------------------+
| 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 transaktionen innehåller en S lås på raden i tempdb.sys.sysschobjs som hänför sig till tabellvariabeln #A1E86130 . droptemp transaktionen kan inte få ett X lås på den här raden på grund av denna konflikt S lås.

Att köra den här frågan upprepade gånger visar att transaction_id för droptemp transaktionen ändras upprepade gånger.

Jag spekulerar i att SQL Server måste allokera dessa interna transaktioner på användarens spids och prioritera dem innan användararbetet utförs. Så sessions-id 53 har fastnat i en konstant cykel där den startar upp en droptemp transaktion, blockeras av användartransaktionen som körs på samma spid. Återställer den interna transaktionen och upprepar sedan processen på obestämd tid.

Detta bekräftas genom att spåra de olika låsnings- och transaktionshändelserna i SQL Server Profiler efter att spiden har hängt sig.

Jag spårade även låsningshändelserna innan dess.

Lås händelseblockering

De flesta av de delade nyckellåsen tas ut av SELECT INTO transaktion på nycklar i sysschobjs släpps omedelbart. Undantaget är det första låset på (246708db8c1f) .

Detta är meningsfullt eftersom planen visar kapslade loops-skanningar av [sys].[sysschobjs].[clst] [o] och eftersom temporära objekt får negativa objekt-id kommer de att vara de första raderna som påträffas i skanningsordning.

Jag stötte också på situationen som beskrivs i OP där att köra en trevägs cross-join först verkar tillåta att den fyrvägs ett lyckas.

De första händelserna i spåret för SELECT INTO transaktion det finns ett helt annat mönster.

Detta var efter en omstart av tjänsten så låsresursvärdena i textdatakolumnen är inte direkt jämförbara.

Istället för att behålla låset på den första nyckeln och sedan ett mönster för att skaffa och släppa efterföljande nycklar, verkar det få många fler lås utan att släppa dem först.

Jag antar att det måste finnas en viss variation i exekveringsstrategin som undviker problemet.

Uppdatera

Anslutningsobjektet jag tog upp om detta har inte markerats som fixat men jag är nu på SQL Server 2012 SP2 och kan nu bara reproducera tillfällig självblockering snarare än permanent. Jag får fortfarande självblockering men efter ett antal misslyckade försök att köra droptemp transaktionen lyckades verkar det gå tillbaka till att bearbeta användartransaktionen. Efter det genomförs systemtransaktionen framgångsrikt. Fortfarande på samma spinn. (åtta försök i ett exempelkörning. Jag är inte säker på om detta kommer att upprepas konsekvent)



  1. Fel vid installation av initialt arkiv i Pentaho Kettle

  2. Hämta bild från Oracle DB

  3. Fungerar Dapper på Mono?

  4. mysql-fråga:visa en sammanfattning av alla gjorda betalningar och summan av fakturor efter datum