排查查询超时错误

现象

假设应用程序从 SQL Server 数据库查询数据。 如果查询未在配置的超时值(通常为 30 秒)内返回任何数据,应用程序将取消查询并生成以下错误消息之一:

  • 超时时间已到。 超时时间在操作完成或服务器没有响应之前已过。 语句已终止。

  • System.Data.SqlClient.SqlException:超时时间已过。 超时时间在操作完成或服务器没有响应之前已过。

解释

这些错误发生在应用程序端。 应用程序设置超时值,如果达到超时值,它将取消查询。 在 SQL Server 端,客户端的查询取消会导致注意事件,错误 3617(MSSQLSERVER_3617)。 如果应用程序端的超时值设置为 0(无时间限制),则数据库引擎执行查询,直到查询完成。

  • 在 .NET Framework System.Data.SqlClient 中,超时值在 CommandTimeout 属性中设置。
  • 在 ODBC API 中,它通过 SQL_ATTR_QUERY_TIMEOUT SQLSetStmtAttr 函数中的属性进行设置。
  • 在 Java Database Connectivity (JDBC) API 中,它是通过 setQueryTimeout 方法设置的。
  • 在 OLEDB 中,它通过 DBPROP_COMMANDTIMEOUT 结构上的 DBPROP 属性进行设置。
  • 在 VBA (Excel) 中,它是通过 ADODB.Command.CommandTimeout 属性设置的。

查询超时不同于连接超时属性。 后者控制等待成功连接的时间,并且查询执行中不涉及。 有关详细信息,请参阅 查询超时与连接超时不同。

疑难解答步骤

到目前为止,查询超时最常见的原因是查询性能不佳。 这意味着查询的运行时间长于预定义的查询超时值。 建议将查询运行更快,这是故障排除的第一个目标。 下面介绍如何检查查询:

  1. 使用 扩展事件SQL 跟踪 标识导致超时错误的查询。 可以将注意力事件与sql_batch_completed扩展rpc_completed事件一起跟踪,并将其关联到同一session_id事件上。 如果观察到已完成事件紧随注意事件,并且已完成事件的持续时间大约对应于超时设置,则表示已确定查询。 下面是一个示例:

    注意

    在此示例中, SELECT 查询运行了近 30 秒并停止。 具有相同会话 ID 的注意事件指示查询已被应用程序取消。

    名称 Session_id Sql_text 持续时间(微秒) Timestamp
    sql_batch_started 54 在操作的右上角选择 … 来自客户 WHERE cid = 192937 Null 2021-09-30 09:50:25.0000
    sql_batch_completed 54 在操作的右上角选择 … 来自客户 WHERE cid = 192937 29999981 2021-09-30 09:50:55.0000
    Attention 54 在操作的右上角选择 … 来自客户 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并检查 options_text 列来比较 SET 选项。 下面是一个示例:

    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 小于预期的查询持续时间。 如果用户的设置正确且仍发生超时,则原因是查询性能问题。 下面是一个 ADO.NET 代码示例,超时值设置为 10 秒:

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

查询超时与连接超时不同

查询超时不同于连接超时或登录超时。当与数据库服务器的初始连接达到预定义超时期限时,将发生连接或登录超时。 在此阶段,尚未将查询提交到服务器。 这些消息是连接或登录超时错误的示例:

  • 连接超时时间已到。 尝试使用预登录握手确认时超时时长已到。 这可能是因为预登录握手失败,或者服务器无法及时返回响应。 尝试连接到此服务器时花费的持续时间是 [Pre-Login] initialization=23; handshake=14979;

  • 超时时间已到。 超时时间在操作完成或服务器没有响应之前已过。 System.ComponentModel.Win32Exception (0x80004005):等待操作已超时。

连接超时值是客户端设置,通常设置为 15 秒。 有关如何排查连接超时问题的详细信息,请参阅 排查连接超时问题。 有关查询超时故障排除,请观看此 视频