今天生产环境遇到了异常: Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding. The statement has been terminated。

超时已过期。操作完成前的超时时间或服务器未响应。声明已被终止

记一次数据库查询超时的原因 Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding. The statement has been terminated

按照以往的经验出现这种情况一般是两种原因

  • 数据库发生死锁,导致执行时间超时
  • 数据量太大,导致在连接时间内,没有完成sql处理,sql需要优化

针对这个异常,首先在测试环境没有复现出来

然后在该操作执行期间查看活动监视器,分析了进程,资源等待和大量资源消耗的情况,然后在数据库分析了锁及阻塞情况,发现这次和以往不一样,并没有死锁或者大量资源消耗及表阻塞的情况。


--是否堵塞
SELECT spid,blocked,waittime,waittype,waitresource,p.dbid,cpu,physical_io,memusage,open_tran 
,status,login_time,last_batch,hostname,program_name,hostprocess,loginame,cmd,text 
FROM master.dbo.sysprocesses p CROSS APPLY sys.dm_exec_sql_text(p.sql_handle) s 
WHERE blocked > 0 OR spid IN(SELECT blocked FROM master.dbo.sysprocesses WHERE blocked > 0) 
go


----查看被锁表:
SELECT
request_session_id spid,
OBJECT_NAME(
resource_associated_entity_id
) tableName
FROM
sys.dm_tran_locks
WHERE
resource_type = 'OBJECT'
ORDER BY request_session_id ASC
--spid 锁表进程
--tableName 被锁表名


--死锁
select 
    er.session_id, 
    CAST(csql.text AS varchar(max)) AS CallingSQL
from master.sys.dm_exec_requests er WITH (NOLOCK) CROSS APPLY fn_get_sql (er.sql_handle) csql
where er.session_id in (select request_session_id from sys.dm_tran_locks where resource_type = 'OBJECT');

然后尝试调大了连接超时时间,依然是这个异常,基本已经可以排除上述的两种情况。

以往的经验行不通了,说明这次情况和以前不一样,在stackoverflow上找到这么一篇文章
记一次数据库查询超时的原因 Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding. The statement has been terminated

发现除了上述两种情况外 数据库的统计信息和/或查询计划缓存不正确也可能会导致这个错误
https://stackoverflow.com/questions/8602395/timeout-expired-the-timeout-period-elapsed-prior-to-completion-of-the-operation

解决方案:

-- 更新表或索引视图的查询优化统计信息
exec sp_updatestats

在官网上找个存储过程

更新表或索引视图的查询优化统计信息。 默认情况下,查询优化器已根据需要更新统计信息以改进查询计划;但在某些情况下,可以通过使用 UPDATE STATISTICS 或存储过程 sp_updatestats 来比默认更新更频繁地更新统计信息,提高查询性能。

更新统计信息可确保查询使用最新的统计信息进行编译。 不过,更新统计信息会导致查询重新编译。 我们建议不要太频繁地更新统计信息,因为需要在改进查询计划和重新编译查询所用时间之间权衡性能。 具体的折衷方案取决于你的应用程序。 UPDATE STATISTICS 可以使用 tempdb 对行样本进行排序以生成统计信息。

记一次数据库查询超时的原因 Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding. The statement has been terminated

官方文档:https://docs.microsoft.com/zh-cn/sql/t-sql/statements/update-statistics-transact-sql?view=sql-server-2017

执行过后发现并没有解决问题:

于是看一了第二条


-- 删除内存中缓存的所有执行计划
dbcc freeproccache


删除计划高速缓存中的所有元素,通过指定计划句柄或 SQL 句柄从计划高速缓存中删除特定计划,或者删除与指定资源池相关联的所有高速缓存条目。

备注

DBCC FREEPROCCACHE 不清除本机编译的存储过程的执行统计信息。 过程高速缓存不包含有关本机编译的存储过程的信息。 从过程执行中收集的任何执行统计信息都将显示在执行统计信息 DMV 中:sys.dm_exec_procedure_stats (Transact-SQL) 和 sys.dm_exec_query_plan (Transact-SQL)。

官网文档:https://docs.microsoft.com/zh-cn/sql/t-sql/database-console-commands/dbcc-freeproccache-transact-sql?view=sql-server-2017

执行完之后,可以正常操作,基本可以确定本次是因为内存中缓存的执行计划有误或者不是最优导致的,但是什么情况会导致这个问题目前还不清楚,需要进一步排查。

相关文章: