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

まとめ

SQL Serverのクエリ タイムアウト エラーは、クライアント アプリケーションのコマンド タイムアウトの有効期限が切れる前にクエリがデータを返さない場合に発生します。 その後、クライアント アプリケーションはクエリをキャンセルし、"タイムアウトの期限切れ" を報告します。この記事では、クエリタイムアウト (接続タイムアウトではなく) が発生したことを確認する方法、attention イベントをsql_batch_completedおよびrpc_completed拡張イベントと関連付けて特定のクエリを識別する方法、および ADO.NET、ODBC、JDBC、OLE DB、および ADO でコマンド タイムアウトを設定する方法について説明します。 最も一般的な原因は低速クエリであるため、推奨される修正は、タイムアウト値のみを上げるのではなく、クエリを調整することです。 この記事は、アプリケーションがタイムアウト エラーを報告し、SQL Serverがアテンション イベント (エラー 3617) を、構成されたタイムアウトと密接に一致する期間で記録する場合に使用します。

クエリ タイムアウト エラーの現象

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

  • タイムアウトに達しました。 処理が完了する前にタイムアウト期間が経過したか、サーバーが応答していません。 ステートメントは終了されました。

  • System.Data.SqlClient.SqlException: タイムアウトに達しました。 処理が完了する前にタイムアウト期間が経過したか、サーバーが応答していません。

クエリ タイムアウト エラーが発生する理由

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

タイムアウト値は、データ アクセス ドライバーまたは API ごとに異なる方法で設定されます。

  • .NET Framework の System.Data.SqlClient では、タイムアウトの値は CommandTimeout プロパティで設定されます。
  • Microsoft。Data.SqlClient は、SQL Serverの現在の ADO.NET ドライバーであり、SqlCommand.CommandTimeout プロパティに設定されています。
  • ODBC API では、SQL_ATTR_QUERY_TIMEOUT 関数の属性によって設定されます。
  • 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に関連付けることができます。 完了したイベントの直後にアテンション イベントが続き、完了したイベントの期間がタイムアウト設定にほぼ対応している場合は、クエリを特定しました。 次に例を示します。

    Note

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

    Name Session_id Sql_text 期間 (マイクロ秒) タイムスタンプ
    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での実行速度が遅いクエリのトラブルシューティング」を参照してください。

    Note

    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;

  • タイムアウトに達しました。 処理が完了する前にタイムアウト期間が経過したか、サーバーが応答していません。 System.ComponentModel.Win32Exception (0x80004005): 待機操作がタイム アウトしました。

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