Delen via


Problemen met query's oplossen die nooit lijken te eindigen op SQL Server

In dit artikel worden de stappen voor probleemoplossing beschreven voor het probleem waarbij u een query hebt die nooit is voltooid, of het voltooien ervan vele uren of dagen kan duren.

Wat is een nooit eindigende query?

Dit document is gericht op query's die blijven uitvoeren of compileren, dat wil gezegd, hun CPU blijft toenemen. Het is niet van toepassing op query's die worden geblokkeerd of wachten op een resource die nooit wordt vrijgegeven (de CPU blijft constant of verandert zeer weinig).

Belangrijk

Als er nog een query overblijft om de uitvoering te voltooien, wordt deze uiteindelijk voltooid. Het kan slechts een paar seconden duren of het kan enkele dagen duren.

De term die nooit eindigt, wordt gebruikt om de perceptie te beschrijven van een query die niet wordt voltooid wanneer de query uiteindelijk wordt voltooid.

Een nooit-eindigende query identificeren

Voer de volgende stappen uit om te bepalen of een query continu wordt uitgevoerd of vastgelopen op een knelpunt:

  1. Voer de volgende query uit.

    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. Controleer de voorbeelduitvoer.

    • De stappen voor probleemoplossing in dit artikel zijn specifiek van toepassing wanneer u een uitvoer ziet die vergelijkbaar is met de volgende, waarbij de CPU evenredig toeneemt met de verstreken tijd, zonder aanzienlijke wachttijden. Het is belangrijk te weten dat wijzigingen logical_reads in dit geval niet relevant zijn, omdat sommige CPU-gebonden T-SQL-aanvragen helemaal geen logische leesbewerkingen uitvoeren (bijvoorbeeld berekeningen of een WHILE lus uitvoeren).

      session_id status cpu_time logical_reads wait_time wait_type
      56 actief 7038 101000 0 NULL
      56 uitvoerbaar 12040 301000 0 NULL
      56 actief 17020 523000 0 NULL
    • Dit artikel is niet van toepassing als u een wachtscenario ziet dat vergelijkbaar is met het volgende scenario waarin de CPU niet erg verandert of verandert, en de sessie wacht op een resource.

      session_id status cpu_time logical_reads wait_time wait_type
      56 onderbroken 0 3 8312 LCK_M_U
      56 onderbroken 0 3 13318 LCK_M_U
      56 onderbroken 0 5 18331 LCK_M_U

    Zie Wachten of knelpunten diagnosticeren voor meer informatie.

Lange compilatietijd

In zeldzame gevallen merkt u misschien dat de CPU continu toeneemt in de loop van de tijd, maar dat wordt niet aangestuurd door de uitvoering van query's. In plaats daarvan kan het worden aangestuurd door een te lange compilatie (het parseren en compileren van een query). Controleer in die gevallen de transaction_name uitvoerkolom en zoek naar een waarde van sqlsource_transform. Deze transactienaam geeft een compilatie aan.

Diagnostische gegevens verzamelen

Voer de volgende stappen uit om diagnostische gegevens te verzamelen met behulp van SQL Server Management Studio (SSMS):

  1. Leg de xml van het geschatte uitvoeringsplan voor query's vast.

  2. Bekijk het queryplan om te zien of er duidelijke indicaties zijn van waar de traagheid vandaan kan komen. Enkele voorbeelden hiervan zijn:

    • Tabel- of indexscans (bekijk geschatte rijen).
    • Geneste lussen die worden aangestuurd door een enorme buitenste tabelgegevensset.
    • Geneste lussen met een grote vertakking aan de binnenkant van de lus.
    • Tafelspools.
    • Functies in de SELECT lijst die lang duren om elke rij te verwerken.
  3. Als de query op elk gewenst moment snel wordt uitgevoerd, kunt u de 'snelle' uitvoeringen vastleggen om het werkelijke XML-uitvoeringsplan te vergelijken.

Methode voor het controleren van de verzamelde plannen

In deze sectie ziet u hoe u de verzamelde gegevens kunt bekijken. Hierbij worden de meerdere XML-queryplannen (met behulp van extensie *.sqlplan) gebruikt die zijn verzameld in SQL Server 2016 SP1 en latere builds en versies.

Volg deze stappen om uitvoeringsplannen te vergelijken:

  1. Open een eerder opgeslagen queryuitvoeringsplanbestand (.sqlplan).

  2. Klik met de rechtermuisknop in een leeg gebied van het uitvoeringsplan en selecteer Showplan vergelijken.

  3. Kies het tweede queryplanbestand dat u wilt vergelijken.

  4. Zoek naar dikke pijlen die duiden op een groot aantal rijen die tussen operators stromen. Selecteer vervolgens de operator vóór of na de pijl en vergelijk het aantal werkelijke rijen tussen twee plannen.

  5. Vergelijk de tweede en derde planning om te zien of de grootste stroom rijen in dezelfde operatoren plaatsvindt.

    Hier volgt een voorbeeld:

    Queryplannen vergelijken in SSMS.

Oplossing

  1. Zorg ervoor dat statistieken worden bijgewerkt voor de tabellen die in de query worden gebruikt.

  2. Zoek naar een ontbrekende indexaanveling in het queryplan en pas deze toe.

  3. Herschrijf de query met het doel om deze te vereenvoudigen:

    • Gebruik meer selectieve WHERE predicaten om de verwerkte gegevens vooraf te verminderen.
    • Breek het uit elkaar.
    • Selecteer enkele onderdelen in tijdelijke tabellen en voeg ze later toe.
    • Verwijder TOP, EXISTSen FAST (T-SQL) in de query's die gedurende zeer lange tijd worden uitgevoerd vanwege het doel van de optimizer-rij. U kunt ook de DISABLE_OPTIMIZER_ROWGOAL hint gebruiken. Zie Row Goals Gone Rogue voor meer informatie.
    • Vermijd het gebruik van CTE's (Common Table Expressions) in gevallen waarin ze instructies combineren in één grote query.
  4. Probeer queryhints te gebruiken om een beter plan te maken:

    • HASH JOIN of MERGE JOIN hint
    • FORCE ORDER tip
    • FORCESEEK tip
    • RECOMPILE
    • GEBRUIKEN PLAN N'<xml_plan>' als u een snel queryplan hebt dat u kunt afdwingen
  5. Gebruik Query Store (QDS) om een goed bekend plan af te dwingen als een dergelijk plan bestaat en of uw SQL Server-versie Query Store ondersteunt.

Wachttijden of knelpunten diagnosticeren

Deze sectie is hier opgenomen als referentie voor het geval uw probleem geen langlopende CPU-rijquery is. U kunt deze gebruiken om problemen met query's op te lossen die lang zijn vanwege wachttijden.

Als u een query wilt optimaliseren die wacht op knelpunten, identificeert u hoe lang de wachttijd is en waar het knelpunt zich bevindt (het wachttype). Zodra het wachttype is bevestigd, vermindert u de wachttijd of elimineert u de wachttijd volledig.

Als u de geschatte wachttijd wilt berekenen, trekt u de CPU-tijd (werktijd) af van de verstreken tijd van een query. Normaal gesproken is de CPU-tijd de werkelijke uitvoeringstijd en het resterende deel van de levensduur van de query wacht.

Voorbeelden van het berekenen van de geschatte wachttijd:

Verstreken tijd (ms) CPU-tijd (ms) Wachttijd (ms)
3200 3000 200
7080 1000 6080

Het knelpunt identificeren of wachten

  • Voer de volgende query uit om historische langwachtquery's te identificeren (bijvoorbeeld >20% van de totale verstreken tijd is wachttijd). Deze query maakt gebruik van prestatiestatistieken voor queryplannen in de cache sinds het begin van SQL Server.

    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
    
  • Voer de volgende query uit om query's met wachttijden langer dan 500 ms te identificeren:

    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
    
  • Als u een queryplan kunt verzamelen, controleert u de WaitStats van de eigenschappen van het uitvoeringsplan in SSMS:

    1. Voer de query uit met Het werkelijke uitvoeringsplan opnemen op.
    2. Klik met de rechtermuisknop op de meest linkse operator op het tabblad Uitvoeringsplan
    3. Selecteer Eigenschappen en vervolgens de eigenschap WaitStats .
    4. Controleer de WaitTimeMs en WaitType.
  • Als u bekend bent met PSSDiag-/SQLdiag - of SQL LogScout LightPerf-/GeneralPerf-scenario's, kunt u een van deze scenario's gebruiken om prestatiestatistieken te verzamelen en wachtquery's op uw SQL Server-exemplaar te identificeren. U kunt de verzamelde gegevensbestanden importeren en de prestatiegegevens analyseren met SQL Nexus.

Verwijzingen om wachttijden te elimineren of te verminderen

De oorzaken en oplossingen voor elk wachttype variëren. Er is geen algemene methode om alle wachttypen op te lossen. Hier volgen artikelen voor het oplossen van veelvoorkomende problemen met wachttypen:

Zie de tabel in Typen wachttijden voor beschrijvingen van veel wachttypen en wat ze aangeven.