Share via


Problemen met query's oplossen die nooit op SQL Server

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

Wat is een oneindige query?

Dit document is gericht op query's die nog steeds worden uitgevoerd of gecompileerd, dat wil gezegd dat hun CPU blijft toenemen. Het is niet van toepassing op query's die zijn geblokkeerd of wachten op een resource die nooit is vrijgegeven (de CPU blijft constant of verandert heel weinig).

Belangrijk

Als een query wordt overgelaten om de uitvoering te voltooien, wordt deze uiteindelijk voltooid. Dit kan slechts enkele seconden duren, of het kan enkele dagen duren.

De term never-ending wordt gebruikt om de perceptie te beschrijven dat een query niet wordt voltooid, terwijl de query uiteindelijk wordt voltooid.

Een oneindige query identificeren

Voer de volgende stappen uit om te bepalen of een query continu wordt uitgevoerd of vastloopt 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 om te weten dat wijzigingen in logical_reads in in dit geval niet relevant zijn, omdat sommige CPU-gebonden T-SQL-aanvragen mogelijk helemaal geen logische leesbewerkingen uitvoeren (bijvoorbeeld berekeningen of een WHILE lus uitvoeren).

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

      session_id Status cpu_time logical_reads wait_time wait_type
      56 Geschorst 0 3 8312 LCK_M_U
      56 Geschorst 0 3 13318 LCK_M_U
      56 Geschorst 0 5 18331 LCK_M_U

    Zie Wachttijden of knelpunten vaststellen voor meer informatie.

Lange compilatietijd

In zeldzame gevallen ziet u misschien dat de CPU in de loop van de tijd voortdurend toeneemt, maar dat dit niet wordt veroorzaakt door de uitvoering van query's. In plaats daarvan kan dit worden aangestuurd door een te lange compilatie (het parseren en compileren van een query). Controleer in dat geval 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 geschatte XML van het queryuitvoeringsplan vast .

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

    • Tabel- of indexscans (bekijk geschatte rijen).
    • Geneste lussen die worden aangedreven door een enorme gegevensset voor buitenste tabellen.
    • Geneste lussen met een grote tak in de binnenkant van de lus.
    • Tabelspools.
    • Functies in de SELECT lijst die lang duren voordat elke rij is verwerkt.
  3. Als de query op elk gewenst moment snel wordt uitgevoerd, kunt u de 'snelle' uitvoeringen vastleggen Werkelijke XML-uitvoeringsplan om te vergelijken.

Methode voor het controleren van de verzamelde plannen

In deze sectie wordt uitgelegd hoe u de verzamelde gegevens kunt controleren. Er worden meerdere XML-queryplannen (met 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 bestand met een queryuitvoeringsplan (.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 een groot aantal rijen tussen operatoren aangeven. Selecteer vervolgens de operator vóór of na de pijl en vergelijk het aantal werkelijke rijen tussen twee plannen.

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

    Hier volgt een voorbeeld:

    Queryplannen vergelijken in SSMS.

Oplossing

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

  2. Zoek naar een ontbrekende indexaan aanbeveling in het queryplan en pas een van de aanbevelingen toe.

  3. Herschrijf de query met als doel deze te vereenvoudigen:

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

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

Wachttijden of knelpunten vaststellen

Deze sectie is hier opgenomen als een referentie voor het geval uw probleem geen langlopende CPU-aansturende query is. U kunt het 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 wacht het resterende deel van de levensduur van de query.

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 langverwachte query'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 te bepalen welke query's momenteel worden uitgevoerd met wachttijden die langer zijn dan 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
    
  • Als u een queryplan kunt verzamelen, controleert u de WaitStats van de eigenschappen van het uitvoeringsplan in SSMS:

    1. Voer de query uit met Werkelijke uitvoeringsplan opnemen op.
    2. Klik met de rechtermuisknop op de operator die het meest links is 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 wachtende query'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 voorkomen of te verminderen

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

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