クエリタイムアウト エラーのトラブルシューティング

現象

アプリケーションがSQL Server データベースからデータを照会することを想定しています。 クエリが構成されたタイムアウト値 (通常は 30 秒) 内にデータを返さない場合、アプリケーションはクエリを取り消し、次のいずれかのエラー メッセージを生成します。

  • タイムアウトの有効期限が切れました。 The timeout period elapsed prior to completion of the operation or the server is not responding. ステートメントは終了しました。

  • System.Data.SqlClient.SqlException: タイムアウトの有効期限が切れました。 The timeout period elapsed prior to completion of the operation or the server is not responding.

説明

これらのエラーは、アプリケーション側で発生します。 アプリケーションはタイムアウト値を設定し、タイムアウトに達するとクエリを取り消します。 SQL Server側では、クライアント側からクエリを取り消すと、Attention イベント (エラー 3617 ) が発生します (MSSQLSERVER_3617)。 アプリケーション側のタイムアウト値が 0 (時間制限なし) に設定されている場合、データベース エンジンはクエリが完了するまで実行します。

  • .NET Framework System.Data.SqlClient では、タイムアウト値が CommandTimeout プロパティで設定されます。
  • ODBC API では、SQL_ATTR_QUERY_TIMEOUTSQLSetStmtAttr 関数の属性を使用して設定されます。
  • Java Database Connectivity (JDBC) API では、 setQueryTimeout メソッドを使用して設定されます。
  • OLEDB では、構造体のプロパティを DBPROP_COMMANDTIMEOUT 介して設定されます DBPROP
  • VBA (Excel) では、ADODB.Command.CommandTimeout プロパティを介して設定されます。

クエリタイムアウトは、接続タイムアウト プロパティとは異なります。 後者は、接続が成功するまでの待機時間を制御し、クエリの実行には関与しません。 詳細については、「 クエリのタイムアウトが接続タイムアウトと同じではない」を参照してください。

トラブルシューティングの手順

クエリタイムアウトの最も一般的な理由は、クエリのパフォーマンスが低い場合です。 つまり、クエリは定義済みのクエリタイムアウト値よりも長く実行されます。 クエリの実行を高速化することが、トラブルシューティングの最初のターゲットとして推奨されます。 確認する方法は次のとおりです。

  1. 拡張イベントまたは SQL トレースを使用して、タイムアウト エラーの原因となるクエリを特定します。 ATTENTION イベントを拡張イベントsql_batch_completedと共にrpc_completedトレースし、同じイベントに関連付けることができますsession_id。 完了したイベントの直後にアテンション イベントが続き、完了したイベントの期間がタイムアウト設定にほぼ対応している場合は、クエリを特定しました。 次に例を示します:

    注:

    この例では、SELECT クエリが ほぼ 30 秒間実行され、停止しました。 同じセッション ID を持つアテンション イベントは、クエリがアプリケーションによって取り消されたことを示します。

    名前 Session_id Sql_text 期間 (マイクロ秒) Timestamp
    sql_batch_started 54 選択します from Customers WHERE cid = 192937 NULL 2021-09-30 09:50:25.0000
    sql_batch_completed 54 選択します from Customers WHERE cid = 192937 29999981 2021-09-30 09:50:55.0000
    注意 54 選択します from Customers WHERE cid = 192937 40000 2021-09-30 09:50:55.0400
  2. SQLCMD または SQL Server Management Studio (SSMS) でクエリを実行してテストします。

  3. SQLCMD と SSMS でもクエリが遅い場合は、トラブルシューティングを行い、クエリのパフォーマンスを向上させます。 詳細については、「SQL Serverでの実行時間の遅いクエリのトラブルシューティング」を参照してください。

    注:

    SQLCMD および SSMS では、タイムアウト値が 0 (時間制限なし) に設定され、クエリをテストおよび調査できます。

  4. SQLCMD と SSMS ではクエリが高速であるが、アプリケーション側では低速である合は、SQLCMD と SSMS で使用されているのと同じ SET オプションを使用するようにクエリを変更します。 拡張イベント トレース (ログインおよび collect_options_text を使用した接続イベント) を収集して SET オプションを比較し、options_text 列を確認します。 次に例を示します:

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

    詳細については、「データベース アプリケーションと SSMS のクエリ パフォーマンスの違いのトラブルシューティング」を参照してください。

  5. CommandTimeout 設定が予想されるクエリ期間よりも小さいかどうかを確認します。 ユーザーの設定が正しくてもタイムアウトが発生する場合は、クエリのパフォーマンスの問題が原因です。 タイムアウト値を 10 秒に設定した ADO.NET コードの例を次に示します。

    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);
                    }
                }
            }
        }
    }
    

クエリ タイムアウトが接続タイムアウトと同じではない

クエリ タイムアウトは、接続タイムアウトまたはログイン タイムアウトとは異なります。接続タイムアウトまたはログイン タイムアウトは、データベース サーバーへの最初の接続が事前定義されたタイムアウト期間に達すると発生します。 この段階では、クエリはサーバーに送信されていません。 次のメッセージは、接続エラーまたはログイン タイムアウト エラーの例です。

  • 接続がタイムアウトしました。 ログイン前のハンドシェイクの確認を実行しようとしている間にタイムアウト期間が過ぎました。 ログイン前のハンドシェイクに失敗したか、サーバーが時間内に応答できなかった可能性があります。 このサーバーへの接続の試行に費やされた時間 - [ログイン前] 初期化 =23; ハンドシェイク =14979;

  • タイムアウト期間が経過しました。 The timeout period elapsed prior to completion of the operation or the server is not responding. System.ComponentModel.Win32Exception (0x80004005): 待ち操作がタイムアウトになりました。

接続タイムアウト値はクライアント側の設定であり、通常は 15 秒に設定されています。 接続タイムアウトのトラブルシューティング方法の詳細については、「接続タイムアウトのトラブルシューティング」を参照してください。 クエリ タイムアウトのトラブルシューティングについては、このビデオをご覧ください。