Нужна помощь в диагностике странных тайм-аутов запросов SQL Server из C#

Я разработал много приложений.NET / SQL Server, но я страдаю от тайм-аутов запросов SQL, которые я не могу понять до конца. У меня есть большой опыт в этой области, чтобы найти оскорбительные запросы и переиндексировать / переписать их. Мое веб-приложение размещено на AWS с использованием RDS для SQL Server и EC2 для веб-приложения. У нас 100-200 уникальных пользователей в день, и база данных составляет около 15 ГБ с парой таблиц> 1 ГБ.

Я вижу исключения в течение дня с сообщением:

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

Запросы, которые страдают от тайм-аутов, так же случайны, как и время, когда они происходят. Кажется, это не совпадает ни с чем очевидным (резервные копии выполняются в течение ночи и т. Д.).

Я попытался взять каждый запрос из приложения C# и запустить его непосредственно в SQL (с такими же параметрами SET, как Arith Abort), и все они выполняются просто отлично. Некоторые из них являются медленными запросами по своей природе, но самый медленный выполняется примерно за 2 секунды и имеет ~400 тыс. Логических чтений. Тем не менее, я также вижу тайм-аут запросов, которые выполняются через 15 мс и имеют < 10 логических чтений.

Самое странное, что я видел, это то, что я взял запрос из веб-приложения и закодировал его в консольное приложение, работающее 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 этим вечером, чтобы исключить это. ( Соединение с удаленным сервером SQL прерывается при обновлении веб-сервера до.net framework 4.6.1)

1 ответ

Решение

После недели напряженного расследования это исправлено!! Он работает уже более 2 часов без единого перерыва:-)

Оказалось, что это какая-то ошибка или несоответствие.NET v4.6.2.

Моя конфигурация была:

  • Веб-версия SQL Server 2017 в AWS RDS
  • .NET v4.6.2
  • Dapper v1.50.5

Мои изменения:

  • Установите.NET 4.7.2 на веб-сервере
  • Обновите Web App и все проекты DLL в Visual Studio для использования.NET 4.7.2 (убедитесь, что файл web.config был обновлен до <httpRuntime targetFramework="4.7.2" />)
  • Обновите Dapper через Nuget до последней версии v1.60.0 (я не думаю, что Dapper был виноват, я просто обновил его, делая все остальное, поскольку он связан с базой данных)

Эти вопросы помогли мне указать в этом направлении:

СПАСИБО В ИНТЕРНЕТЕ - КАК НА ЗЕМЛЕ Я КОДИРОВАЛ ПЕРЕД ТЕБЕМ, ЧТО ВЫ ПРИШЛИ

Другие вопросы по тегам