我开发了许多 .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 服务器的连接中断)