Dela via


Felsöka frågor som aldrig verkar slutföras i SQL Server

Den här artikeln beskriver felsökningsstegen för problemet där du har en fråga som aldrig verkar slutföras, eller så kan det ta många timmar eller dagar att slutföra den.

Vad är en oändlig fråga?

Det här dokumentet fokuserar på frågor som fortsätter att köras eller kompileras, dvs. deras CPU fortsätter att öka. Den gäller inte för frågor som blockeras eller väntar på en resurs som aldrig släpps (processorn förblir konstant eller ändras mycket lite).

Viktigt!

Om en fråga är kvar för att slutföra körningen slutförs den så småningom. Det kan ta bara några sekunder, eller så kan det ta flera dagar.

Termen never-ending används för att beskriva uppfattningen av en fråga som inte slutförs när frågan i själva verket slutförs.

Identifiera en oändlig fråga

Följ dessa steg för att identifiera om en fråga körs kontinuerligt eller fastnar på en flaskhals:

  1. Kör följande fråga:

    DECLARE @cntr int = 0
    
    WHILE (@cntr < 3)
    BEGIN
        SELECT TOP 10 s.session_id,
                        r.status,
                        r.wait_time,
                        r.wait_type,
                        r.wait_resource,
                        r.cpu_time,
                        r.logical_reads,
                        r.reads,
                        r.writes,
                        r.total_elapsed_time / (1000 * 60) 'Elaps M',
                        SUBSTRING(st.TEXT, (r.statement_start_offset / 2) + 1,
                        ((CASE r.statement_end_offset
                            WHEN -1 THEN DATALENGTH(st.TEXT)
                            ELSE r.statement_end_offset
                        END - r.statement_start_offset) / 2) + 1) AS statement_text,
                        COALESCE(QUOTENAME(DB_NAME(st.dbid)) + N'.' + QUOTENAME(OBJECT_SCHEMA_NAME(st.objectid, st.dbid)) 
                        + N'.' + QUOTENAME(OBJECT_NAME(st.objectid, st.dbid)), '') AS command_text,
                        r.command,
                        s.login_name,
                        s.host_name,
                        s.program_name,
                        s.last_request_end_time,
                        s.login_time,
                        r.open_transaction_count,
                        atrn.name as transaction_name,
                        atrn.transaction_id,
                        atrn.transaction_state
            FROM sys.dm_exec_sessions AS s
            JOIN sys.dm_exec_requests AS r ON r.session_id = s.session_id 
                    CROSS APPLY sys.Dm_exec_sql_text(r.sql_handle) AS st
            LEFT JOIN (sys.dm_tran_session_transactions AS stran 
                 JOIN sys.dm_tran_active_transactions AS atrn
                    ON stran.transaction_id = atrn.transaction_id)
            ON stran.session_id =s.session_id
            WHERE r.session_id != @@SPID
            ORDER BY r.cpu_time DESC
    
        SET @cntr = @cntr + 1
    WAITFOR DELAY '00:00:05'
    END
    
  2. Kontrollera exempelutdata.

    • Felsökningsstegen i den här artikeln gäller särskilt när du ser utdata som liknar följande där processorn ökar proportionellt med den förflutna tiden, utan betydande väntetider. Det är viktigt att observera att ändringar i logical_reads inte är relevanta i det här fallet eftersom vissa CPU-bundna T-SQL-begäranden kanske inte gör några logiska läsningar alls (till exempel utför beräkningar eller en WHILE loop).

      session_id status cpu_time logical_reads wait_time wait_type
      56 körs 7038 101000 0 NULL
      56 körbar 12040 301000 0 NULL
      56 körs 17020 523000 0 NULL
    • Den här artikeln är inte tillämplig om du ser ett väntescenario som liknar följande där processorn inte ändras eller ändras mycket lite, och sessionen väntar på en resurs.

      session_id status cpu_time logical_reads wait_time wait_type
      56 upphängd 0 3 8312 LCK_M_U
      56 upphängd 0 3 13318 LCK_M_U
      56 upphängd 0 5 18331 LCK_M_U

    Mer information finns i Diagnostisera väntetider eller flaskhalsar.

Lång kompileringstid

I sällsynta fall kan du observera att processorn ökar kontinuerligt med tiden, men det drivs inte av frågekörning. I stället kan den drivas av en alltför lång kompilering (parsning och kompilering av en fråga). I dessa fall kontrollerar du kolumnen transaction_name utdata och letar efter värdet sqlsource_transform. Det här transaktionsnamnet anger en kompilering.

Samla in diagnostikdata

Följ dessa steg för att samla in diagnostikdata med hjälp av SQL Server Management Studio (SSMS):

  1. Samla in xml-koden för den uppskattade frågekörningsplanen .

  2. Granska frågeplanen för att se om det finns några tydliga indikationer på var långsamheten kan komma ifrån. Vanliga exempel är:

    • Tabell- eller indexgenomsökningar (titta på uppskattade rader).
    • Kapslade loopar som drivs av en enorm datauppsättning i en yttre tabell.
    • Kapslade loopar med en stor gren på den inre sidan av slingan.
    • Tabellpooler.
    • Funktioner i SELECT listan som tar lång tid att bearbeta varje rad.
  3. Om frågan körs snabbt när som helst kan du samla in de "snabba" körningarna av den faktiska XML-körningsplanen som ska jämföras.

Metod för att granska insamlade planer

Det här avsnittet visar hur du granskar insamlade data. Den använder flera XML-frågeplaner (med tillägget *.sqlplan) som samlats in i SQL Server 2016 SP1 och senare versioner.

Följ dessa steg för att jämföra körningsplaner:

  1. Öppna en tidigare sparad frågekörningsplanfil (.sqlplan).

  2. Högerklicka i ett tomt område i körningsplanen och välj Jämför Showplan.

  3. Välj den andra frågeplansfilen som du vill jämföra.

  4. Leta efter tjocka pilar som anger ett stort antal rader som flödar mellan operatorer. Välj sedan operatorn före eller efter pilen och jämför antalet faktiska rader mellan två planer.

  5. Jämför den andra och tredje planen för att se om det största flödet av rader sker i samma operatorer.

    Här är ett exempel:

    Jämför frågeplaner i SSMS.

Åtgärd

  1. Se till att statistiken uppdateras för de tabeller som används i frågan.

  2. Leta efter en indexrekommendations som saknas i frågeplanen och tillämpa eventuella.

  3. Skriv om frågan med målet för att förenkla den:

    • Använd mer selektiva WHERE predikat för att minska de data som bearbetas i förväg.
    • Bryt isär den.
    • Välj vissa delar i temporära tabeller och anslut dem senare.
    • Ta bort TOP, EXISTSoch FAST (T-SQL) i de frågor som körs under mycket lång tid på grund av optimerarens radmål. Du kan också använda tipset DISABLE_OPTIMIZER_ROWGOAL . Mer information finns i Row Goals Gone Rogue.
    • Undvik att använda vanliga tabelluttryck (CTE) i sådana fall när de kombinerar instruktioner till en enda stor fråga.
  4. Prova att använda frågetips för att skapa en bättre plan:

    • HASH JOIN eller MERGE JOIN tips
    • FORCE ORDER antydan
    • FORCESEEK antydan
    • RECOMPILE
    • ANVÄND PLAN N'<xml_plan>' om du har en snabb frågeplan som du kan framtvinga
  5. Använd Query Store (QDS) för att framtvinga en bra känd plan om en sådan plan finns och om din SQL Server-version stöder Query Store.

Diagnostisera väntetider eller flaskhalsar

Det här avsnittet ingår här som referens om problemet inte är en tidskrävande CPU-körningsfråga. Du kan använda den för att felsöka frågor som är långa på grund av väntetider.

Om du vill optimera en fråga som väntar på flaskhalsar identifierar du hur lång väntetiden är och var flaskhalsen är (väntetypen). När väntetypen har bekräftats kan du minska väntetiden eller eliminera väntetiden helt.

Om du vill beräkna den ungefärliga väntetiden subtraherar du CPU-tiden (arbetstiden) från den förflutna tiden för en fråga. Cpu-tiden är vanligtvis den faktiska körningstiden och den återstående delen av frågans livslängd väntar.

Exempel på hur du beräknar ungefärlig väntetid:

Förfluten tid (ms) CPU-tid (ms) Väntetid (ms)
3200 3000 200
7080 1000 6080

Identifiera flaskhalsen eller vänta

  • Kör följande fråga för att identifiera historiska frågor som väntar länge (till exempel >20 % av den totala förflutna tiden är väntetiden). Den här frågan använder prestandastatistik för cachelagrade frågeplaner sedan SQL Server startades.

    SELECT t.text,
             qs.total_elapsed_time / qs.execution_count
             AS avg_elapsed_time,
             qs.total_worker_time / qs.execution_count
             AS avg_cpu_time,
             (qs.total_elapsed_time - qs.total_worker_time) / qs.execution_count
             AS avg_wait_time,
             qs.total_logical_reads / qs.execution_count
             AS avg_logical_reads,
             qs.total_logical_writes / qs.execution_count
             AS avg_writes,
             qs.total_elapsed_time
             AS cumulative_elapsed_time
    FROM sys.dm_exec_query_stats qs
             CROSS apply sys.Dm_exec_sql_text (sql_handle) t
    WHERE (qs.total_elapsed_time - qs.total_worker_time) / qs.total_elapsed_time
             > 0.2
    ORDER BY qs.total_elapsed_time / qs.execution_count DESC
    
  • Kör följande fråga för att identifiera att frågor körs med längre väntetider än 500 ms:

    SELECT r.session_id, r.wait_type, r.wait_time AS wait_time_ms
    FROM sys.dm_exec_requests r 
       JOIN sys.dm_exec_sessions s ON r.session_id = s.session_id 
    WHERE wait_time > 500
    AND is_user_process = 1
    
  • Om du kan samla in en frågeplan kontrollerar du WaitStats från körningsplanens egenskaper i SSMS:

    1. Kör frågan med Inkludera faktisk körningsplan på.
    2. Högerklicka på operatorn längst till vänster på fliken Körningsplan
    3. Välj Egenskaper och sedan egenskapen WaitStats .
    4. Kontrollera WaitTimeMs och WaitType.
  • Om du är bekant med scenarier med PSSDiag/SQLdiag eller SQL LogScout LightPerf/GeneralPerf kan du använda någon av dem för att samla in prestandastatistik och identifiera väntande frågor på din SQL Server-instans. Du kan importera de insamlade datafilerna och analysera prestandadata med SQL Nexus.

Referenser för att eliminera eller minska väntetider

Orsakerna och lösningarna för varje väntetyp varierar. Det finns ingen allmän metod för att lösa alla väntetyper. Här följer artiklar om hur du felsöker och löser vanliga problem med väntetyp:

Beskrivningar av många väntetyper och vad de anger finns i tabellen i Typer av väntetider.