次の方法で共有


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

現象

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

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

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

説明

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

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

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

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

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

  1. Extended Events または SQL Trace を使用して、タイムアウト エラーの原因となるクエリを特定します。 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 秒に設定されます。 接続タイムアウトのトラブルシューティング方法の詳細については、「接続タイムアウトのトラブルシューティングを参照してください。 クエリ タイムアウトのトラブルシューティングについては、この video をご覧ください。