Rozwiązywanie problemów z błędami limitu czasu zapytania

Symptomy

Załóżmy, że aplikacja wysyła zapytania o dane z bazy danych serwera SQL. Jeśli zapytanie nie zwraca żadnych danych w ramach skonfigurowanej wartości limitu czasu (zazwyczaj 30 sekund), aplikacja anuluje zapytanie i generuje jeden z następujących komunikatów o błędach:

  • Limit czasu wygasł. Limit czasu wygasł przed zakończeniem operacji lub serwer nie odpowiada. Instrukcja została zakończona.

  • System.Data.SqlClient.SqlException: Limit czasu wygasł. Limit czasu wygasł przed zakończeniem operacji lub serwer nie odpowiada.

Objaśnienie

Te błędy występują po stronie aplikacji. Aplikacja ustawia wartość limitu czasu, a jeśli limit czasu zostanie osiągnięty, anuluje zapytanie. Po stronie serwera SQL anulowanie zapytania po stronie klienta powoduje zdarzenie Uwaga, błąd 3617 (MSSQLSERVER_3617). Jeśli wartość limitu czasu po stronie aplikacji jest ustawiona na 0 (bez limitu czasu), aparat bazy danych wykonuje zapytanie do momentu jego ukończenia.

  • W platformie .NET Framework instrukcja System.Data.SqlClient, wartość limitu czasu jest ustawiana we właściwości CommandTimeout.
  • W interfejsie API ODBC jest on ustawiany za pomocą atrybutu SQL_ATTR_QUERY_TIMEOUT w funkcji SQLSetStmtAttr.
  • W interfejsie API łączności z bazą danych Java (JDBC) jest on ustawiany za pomocą metody setQueryTimeout.
  • W środowisku OLEDB jest on ustawiany za pośrednictwem właściwości DBPROP_COMMANDTIMEOUT w strukturze DBPROP.
  • W języku VBA (Excel) jest on ustawiany za pośrednictwem właściwości ADODB.Command.CommandTimeout.

Limit czasu zapytania różni się od właściwości limitu czasu połączenia. Ten ostatni określa czas oczekiwania na pomyślne połączenie i nie jest związany z wykonywaniem zapytań. Aby uzyskać więcej informacji, zobacz artykuł Limit czasu zapytania nie jest tożsamy z limitem czasu połączenia.

Czynności umożliwiające rozwiązywanie problemów

Zdecydowanie najczęstszą przyczyną przekroczenia limitu czasu zapytań jest złe zaprojektowanie zapytań. Oznacza to, że zapytanie jest uruchamiane dłużej niż wstępnie zdefiniowana wartość limitu czasu zapytania. Szybsze wykonanie zapytania jest zalecanym pierwszym celem rozwiązywania problemów. Poniżej przedstawiono sposób sprawdzania zapytań:

  1. Użyj narzędzi Extended Events lub SQL Trace, aby zidentyfikować zapytania, które powodują błędy przekroczenia limitu czasu. Zdarzenie Uwaga można śledzić razem ze zdarzeniami wydłużonymi sql_batch_completed i rpc_completed oraz skorelować je w tym samym identyfikatorem session_id. Jeśli zauważysz, że po zakończonym zdarzeniu natychmiast następuje zdarzenie uwagi, a czas trwania ukończonego zdarzenia odpowiada w przybliżeniu ustawieniu limitu czasu, zapytanie zostało zidentyfikowane. Oto przykład:

    Uwaga

    W tym przykładzie zapytanie SELECT trwało prawie dokładnie 30 sekund i zostało zatrzymane. Zdarzenie Uwaga o tym samym identyfikatorze sesji wskazuje, że zapytanie zostało anulowane przez aplikację.

    Name (Nazwa) Session_id Sql_text Czas trwania (mikrosekundy) Znacznik czasu
    sql_batch_started 54 Wybierz pozycję … od klientów, gdzie identyfikator cid = 192937 NULL 2021-09-30 09:50:25.0000
    sql_batch_completed 54 Wybierz pozycję … od klientów, gdzie identyfikator cid = 192937 29999981 2021-09-30 09:50:55.0000
    Uwaga 54 Wybierz pozycję … od klientów, gdzie identyfikator cid = 192937 40000 2021-09-30 09:50:55.0400
  2. Wykonywanie i testowanie zapytań w programie SQLCMD lub w programie SQL Server Management Studio (SSMS).

  3. Jeśli zapytania są również powolne w programach SQLCMD i SSMS, rozwiąż problemy i popraw wydajność zapytań. Aby uzyskać szczegółowe informacje, zobacz Rozwiązywanie problemów z wolno działającymi zapytaniami w SQL Server

    Uwaga

    W programach SQLCMD i SSMS wartość limitu czasu jest ustawiona na 0 (bez limitu czasu), a zapytania można przetestować i zbadać.

  4. Jeśli zapytania są szybkie w programach SQLCMD i SSMS, ale działają wolno po stronie aplikacji, zmień zapytania tak, aby używały tych samych opcji SET używanych w programach SQLCMD i SSMS. Porównaj opcje SET, zbierając ślad w narzędziu Extended Events (logowanie i łączenie zdarzeń ze znacznikiem collect_options_text) i sprawdź kolumnę options_text. Oto przykład:

    ALTER EVENT SESSION [setOptions] ON SERVER 
    ADD EVENT sqlserver.existing_connection(SET collect_options_text=(1) 
        ACTION(package0.event_sequence,package0.last_error,sqlos.system_thread_id,sqlserver.context_info,sqlserver.session_id,sqlserver.sql_text)), 
    ADD EVENT sqlserver.login(SET collect_options_text=(1)
        ACTION(sqlos.system_thread_id,sqlserver.context_info,sqlserver.sql_text))
    

    Aby uzyskać więcej informacji, zobacz Rozwiązywanie problemów z różnicą wydajności zapytań między aplikacją bazy danych a programem SSMS.

  5. Sprawdź, czy ustawienie CommandTimeout jest mniejsze niż oczekiwany czas trwania zapytania. Jeśli ustawienie użytkownika jest poprawne i przekraczanie limitów czasu nadal występuje, to jest to spowodowane problemem z wydajnością zapytania. Oto przykład kodu ADO.NET z wartością limitu czasu ustawioną na 10 sekund:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Data.SqlClient;
    using System.Data;
    
    namespace ConsoleApplication6
    {
        class Program
        {
            static void Main()
            {
                string ConnectionString = "Data Source=.\sql2019;Integrated Security=SSPI;Initial Catalog=tempdb;";
                string queryString = "exec test";
    
                using (SqlConnection connection = new SqlConnection(ConnectionString))
                {
                    connection.Open();
                    SqlCommand command = new SqlCommand(queryString, connection);
    
                    // Setting command timeout to 10 seconds
                    command.CommandTimeout = 10;
                    //command.ExecuteNonQuery();
                    try {
                        command.ExecuteNonQuery();
                    }
                    catch (SqlException e) {
                        Console.WriteLine("Got expected SqlException due to command timeout ");
                        Console.WriteLine(e);
                    }
                }
            }
        }
    }
    

Limit czasu zapytania nie jest tożsamy z limitem czasu połączenia

Limit czasu zapytania różni się od limitu czasu połączenia lub limitu czasu logowania. Limit czasu połączenia lub logowania występuje, gdy początkowe połączenie z serwerem bazy danych osiągnie wstępnie zdefiniowany limit czasu. Na tym etapie żadne zapytanie nie zostało przesłane do serwera. Te komunikaty są przykładami błędu przekroczenia limitu czasu połączenia lub logowania:

  • Limit czasu połączenia wygasł. Limit czasu upłynął podczas próby skorzystania z potwierdzenia uzgadniania przed logowaniem. Może to być spowodowane tym, że uzgadnianie przed logowaniem nie powiodło się lub serwer nie mógł odpowiedzieć w czasie. Czas trwania próby nawiązania połączenia z tym serwerem to [Pre-Login] initialization=23; handshake=14979;

  • Limit czasu wygasł. Limit czasu wygasł przed zakończeniem operacji lub serwer nie odpowiada. System.ComponentModel.Win32Exception (0x80004005): Upłynął limit czasu operacji oczekiwania.

Wartość limitu czasu połączenia jest ustawieniem po stronie klienta i zazwyczaj jest ustawione na 15 sekund. Aby uzyskać więcej informacji na temat rozwiązywania problemów z przekroczeniem limitu czasu połączenia, zobacz artykuł Rozwiązywanie problemów z przekroczeniem limitu czasu połączenia. Aby rozwiązać problem z przekroczeniem limitu czasu zapytań, obejrzyj ten materiał wideo.