【故障公告】SQL语句执行超时引起网站首页访问故障

很是抱歉,今天早上 6:37~8:15 期间,因为获取网站首页博文列表的 SQL 语句出现突发的查询超时问题,形成访问网站首页时出现 500 错误,由此给您带来麻烦,请您谅解。html

故障的状况是这样的。web

故障期间日志中记录了大量下面的错误。数据库

2020-02-03 06:37:24.635 [Error] An unhandled exception has occurred while executing the request.
/
Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware
System.Data.SqlClient.SqlException (0x80131904): Timeout expired.  The timeout period elapsed prior to completion of the operation or the server is not responding.
 ---> System.ComponentModel.Win32Exception (258): Unknown error 258
   at System.Data.SqlClient.SqlCommand.<>c.<ExecuteDbDataReaderAsync>b__126_0(Task`1 result)

数据库服务器(阿里云 RDS SQL Server 2016 实例)的 CPU 消耗突增。api

数据库服务器的 IOPS 暴增。缓存

经过阿里云 RDS 控制台的 CloudDBA 能够查看到故障期间获取首页博文的 SQL 语句被执行了3万屡次,执行这么屡次是因为查询超时,没法创建缓存,每次请求都要访问数据库。服务器

发现故障后,咱们经过阿里云 RDS 的主备切换恢复了正常。性能

通过对故障的排查分析,锁定的最大嫌疑对象是 SQL Server 参数嗅探(详见园子里的博文 什么是 SQL Server 参数嗅探)。网站

对于这种由于重用他人生成的执行计划而致使的水土不服现象,SQL Server 有一个专有名词,叫“参数嗅探 parameter sniffing”。阿里云

并且咱们找到了引起 SQL Server 参数嗅探问题的条件。spa

在咱们的 open api 中提供了获取首页博文列表的 web api ,但没有限制能够获取的最大博文数,也就是下面的 ItemCount 参数(除了 open api ,其余地方调用时 ItemCount 值都是 20 )。

SELECT TOP (@ItemCount)

假若有人调用 open api 时给 ItemCount 传了一个很大的值,好比 20000 ,虽然调用的是一样的 SQL 语句,但因为 ItemCount 的值不一样, SQL Server 可能会生成相差很大的执行计划,对于 ItemCount 20000 性能比较好的执行计划,对于 ItemCount 20 可能性能极差。若是查询 ItemCount 20000 时生成的执行计划被缓存下来,查询 ItemCount 20 时继续使用这个执行计划,就会出现原本好好的 SQL 查询忽然变得性能极差。咱们今天遇到的故障极可能就是这个缘由,并且故障时就一个 SQL 语句出现问题(正好就这个 SQL 查询缓存了水土不服的执行计划),其余都正常,也验证了这个猜想。

经过此次故障,咱们吸收的教训是必定要在代码中对 ItemCount 与 PageSize 的最大值进行限制,它不单单是带来没必要要的低性能查询,并且可能会由于 SQL Server 参数嗅探问题拖垮整个数据库。