6

我开发了许多 .NET / SQL Server 应用程序,但我遇到了无法深入了解的 SQL 查询超时。我在查找有问题的查询并重新索引/重写它们方面有很多经验。我的 Web 应用程序使用用于 SQL Server 的 RDS 和用于 Web 应用程序的 EC2 托管在 AWS 上。我们每天有 100-200 个唯一用户,数据库大约 15GB,有几个表 > 1GB。

我一整天都看到异常消息:

'Execution Timeout Expired.  The timeout period elapsed prior to completion of the operation or the server is not responding.'

遭受超时的查询与超时发生的时间一样随机。它似乎与任何明显的事情不符(备份在一夜之间运行等)。

我尝试从 C# 应用程序中获取每个查询并直接在 SQL 中运行它(使用与 Arith Abort 相同的 SET 选项),它们都运行得很好。有些本质上是较慢的查询,但最慢的查询在大约 2 秒内运行,并且具有约 400k 的逻辑读取。但是,我还看到在 15 毫秒内运行且逻辑读取次数小于 10 的查询超时。

我见过的最奇怪的事情是,我从 Web 应用程序中获取了一个查询并将其编码到一个控制台应用程序中,该应用程序已经运行了 24 小时,每秒调用一次查询。即使我已经看到主系统在运行期间对同一查询有超时,它也没有出现过任何异常/超时。

我最近将 RDS 服务器升级到 M5 Large 并且所有索引每天都会在一夜之间重建。我在某个时候运行了 DBCC FREEPROCCACHE,以确保没有过时的查询计划导致问题。

我觉得这是参数嗅探,或者我最后的想法是硬件/网络故障,但那真的是抓住了稻草!

我得到的堆栈跟踪看起来像是在查询中,而不是在连接阶段。

at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)  
   at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)  
   at System.Data.SqlClient.TdsParserStateObject.ReadSniError(TdsParserStateObject stateObj, UInt32 error)  
   at System.Data.SqlClient.TdsParserStateObject.ReadSniSyncOverAsync()  
   at System.Data.SqlClient.TdsParserStateObject.TryReadNetworkPacket()  
   at System.Data.SqlClient.TdsParserStateObject.TryPrepareBuffer()  
   at System.Data.SqlClient.TdsParserStateObject.TryReadByteArray(Byte[] buff, Int32 offset, Int32 len, Int32& totalRead)  
   at System.Data.SqlClient.TdsParserStateObject.TryReadString(Int32 length, String& value)  
   at System.Data.SqlClient.TdsParser.TryReadSqlStringValue(SqlBuffer value, Byte type, Int32 length, Encoding encoding, Boolean isPlp, TdsParserStateObject stateObj)  
   at System.Data.SqlClient.TdsParser.TryReadSqlValue(SqlBuffer value, SqlMetaDataPriv md, Int32 length, TdsParserStateObject stateObj, SqlCommandColumnEncryptionSetting columnEncryptionOverride, String columnName)  
   at System.Data.SqlClient.SqlDataReader.TryReadColumnInternal(Int32 i, Boolean readHeaderOnly)  
   at System.Data.SqlClient.SqlDataReader.TryReadColumn(Int32 i, Boolean setTimeout, Boolean allowPartiallyReadColumn)  
   at System.Data.SqlClient.SqlDataReader.GetValueInternal(Int32 i)  
   at System.Data.SqlClient.SqlDataReader.GetValue(Int32 i) 

任何有助于了解这一点的技术将不胜感激,因为它令人不安,我担心它会突然变得更糟。

谢谢

编辑 1

我试图通过每 10 毫秒运行一次测试应用程序(如上)并同时在 SSMS 中运行缓慢的阻塞事务来在本地创建相同的问题。

从应用程序查询

SELECT TOP 10 *
FROM MyTable
WHERE LastModifiedBy = 'Stu'

SSMS 中的查询

BEGIN TRAN
UPDATE TOP (10000) MyTable SET LastModifiedBy = 'Me' where LastModifiedBy = 'Me'
WAITFOR DELAY '00:00:35'
COMMIT

当出现此错误时,我会在 SQL Profiler 中看到我通常期望看到的内容,其中应用程序查询恰好需要 30000 毫秒,并且在应用程序中出现异常。但是,有用的输出是堆栈跟踪与我在生产中看到的不同(上图)。

at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction) 
   at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction) 
   at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose) 
   at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady) 
   at System.Data.SqlClient.SqlDataReader.TryConsumeMetaData() 
   at System.Data.SqlClient.SqlDataReader.get_MetaData() 
   at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString, Boolean isInternal, Boolean forDescribeParameterEncryption, Boolean shouldCacheForAlwaysEncrypted) 
   at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async, Int32 timeout, Task& task, Boolean asyncWrite, Boolean inRetry, SqlDataReader ds, Boolean describeParameterEncryptionRequest) 
   at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, TaskCompletionSource`1 completion, Int32 timeout, Task& task, Boolean& usedCache, Boolean asyncWrite, Boolean inRetry) 
   at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method) 
   at System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method) 
   at System.Data.SqlClient.SqlCommand.ExecuteDbDataReader(CommandBehavior behavior) 
   at System.Data.Common.DbCommand.System.Data.IDbCommand.ExecuteReader(CommandBehavior behavior) 

我正在阅读此堆栈跟踪,因为查询从未开始执行,因为它仍在尝试读取查询的元数据。但是,这与生产中的堆栈跟踪(在我看来)似乎处于从列读取数据的中间,但在执行过程中超时。

我也一直在阅读我们正在使用的 .NET 4.6.2 版本。我今晚会将所有内容升级到 4.7.2 以排除这种情况。(将 Web 服务器升级到 .net 框架 4.6.1 时与远程 SQL 服务器的连接中断

4

1 回答 1

0

经过一周紧张的调查,它已修复!!它现在已经运行了 2 多个小时,没有一次超时 :-)

原来是某种错误或与 .NET v4.6.2 不匹配。

我的配置是:

  • AWS RDS 上的 SQL Server 2017 网络版
  • .NET v4.6.2
  • 小巧玲珑 v1.50.5

我的更改是:

  • 在 Web 服务器上安装 .NET 4.7.2
  • 升级 Web 应用程序和 Visual Studio 中的所有 DLL 项目以使用 .NET 4.7.2(确保 web.config 已更新为<httpRuntime targetFramework="4.7.2" />
  • 通过 Nuget 将 Dapper 升级到最新的 v.1.60.0(我不认为 Dapper 有问题,我只是在做其他所有事情的同时升级它,因为它与数据库相关)

这些问题帮助我指出了这个方向:

谢谢互联网 - 在你来之前我到底是怎么编码的

于 2019-04-04T08:11:06.560 回答