Несколько одновременных тайм-аутов подключения SQL в многопоточной службе Windows

У меня есть многопоточная служба Windows, которую я разработал с помощью VS 2010 (.NET 4.0), которая может иметь от нескольких до нескольких десятков потоков, каждый из которых извлекает данные с медленного сервера через Интернет, а затем использует локальную базу данных для записи этого данные (поэтому процесс привязан к Интернету, а не к локальной сети или ЦП).

С некоторой регулярностью я получаю флуд/шквал/всплеск следующей ошибки из нескольких потоков одновременно:

System.Data.SqlClient.SqlException (0x80131904): время ожидания истекло. Время ожидания истекло до завершения операции или сервер не отвечает.

Стек вызовов для этой ошибки обычно:

в System.Data.ProviderBase.DbConnectionPool.GetConnection (DbConnection owningObject)

в System.Data.ProviderBase.DbConnectionFactory.GetConnection(DbConnection owningConnection)

в System.Data.ProviderBase.DbConnectionClosed.OpenConnection (DbConnection externalConnection, DbConnectionFactory connectionFactory)

в System.Data.SqlClient.SqlConnection.Open()

Я не указываю время ожидания подключения в строке подключения, и в этой базе данных работают другие приложения и процессы. Кто-нибудь сталкивался с таким поведением, и если да, то что было сделано, чтобы предотвратить это?

Наиболее часто вызываемый метод на моем уровне доступа к данным выглядит так, и все другие мои методы DAL следуют тому же подходу:

using (SqlConnection con = new SqlConnection(GetConnectionString()))
using (SqlCommand cmd = new SqlCommand("AddGdsMonitorLogEntry", con))
{
    cmd.CommandType = CommandType.StoredProcedure;

    /* setting cmd.Parameters [snipped] */

    // We have been getting some timeouts writing to the log; wait a little longer than the default.
    cmd.CommandTimeout *= 4;

    con.Open();

    cmd.ExecuteNonQuery();
}

Большое спасибо!

ИЗМЕНИТЬ

Учитывая комментарии о том, что это происходит в зеркальных средах, я действительно должен упомянуть, что рассматриваемая база данных является зеркальной. Он помечен в SSMS как основной, синхронизированный, в режиме высокой безопасности без автоматического перехода на другой ресурс (синхронный).

ИЗМЕНИТЬ 26.05.11

Я не вижу в журналах SQL Server ничего, что указывало бы на какие-либо проблемы. (У меня нет доступа к средству просмотра событий Windows на этом сервере, но я попросил кого-нибудь найти меня.)


person ALEXintlsos    schedule 04.05.2011    source источник
comment
Я также вижу точно такую ​​​​же проблему с той же трассировкой стека. База данных, к которой он подключается, является зеркальной, а в строке подключения указан отказоустойчивый партнер. Мне не удалось воспроизвести ту же проблему с моего локального рабочего стола, открытие нескольких подключений и никогда их не закрытие приводит к другому сообщению об исключении.   -  person BrandonAGr    schedule 14.05.2011
comment
Эти ссылки сообщают об аналогичной проблеме, но ни одна из них не предлагает решения: 1 2 3 4 5   -  person BrandonAGr    schedule 14.05.2011
comment
Я думаю, что проблема не в соединении, клиенте или базе данных. Но в запросах выполнено. Проверьте их, например. собирать статистику, какие SP/запросы чаще вызывают исключение   -  person abatishchev    schedule 15.05.2011
comment
В качестве обновления проблемы, которую я видел, добавление Connection Timeout = 500; к строке подключения, кажется, предотвращает проблему. Таким образом, похоже, что это действительно может быть проблема с тем, что база данных слишком долго отвечает, B = но я действительно не хочу оставлять такой высокий тайм-аут.   -  person BrandonAGr    schedule 16.05.2011
comment
@AleXintlsos - так каково было ваше окончательное решение этой проблемы? Увеличить тайм-аут (до чего?) и увеличить пул соединений?   -  person VoodooChild    schedule 09.05.2013


Ответы (5)


Согласно сообщение в блоге MSDN, только что созданное сегодня (ура для Google!):

Корпорация Майкрософт подтвердила, что это проблема текущего выпуска ADO.NET. Эта проблема будет исправлена ​​в версии ADO.NET, поставляемой с Visual Studio 2011.

Тем временем мы просим использовать следующие обходные пути:

  1. Увеличьте время ожидания строки подключения до 150 секунд. Это даст первой попытке достаточно времени для подключения (150 * .08 = 12 секунд).

  2. Добавьте MinPool Size=20 в строку подключения. Это всегда будет поддерживать минимум 20 подключений в пуле, и будет меньше шансов создать новое подключение, что уменьшит вероятность этой ошибки.

  3. Улучшить производительность сети. Обновите драйверы сетевой карты до последней версии прошивки. Мы наблюдали задержку в сети, когда ваша сетевая карта несовместима с определенными настройками Scalable Networking Pack. Если вы используете Windows Vista SP1 или более позднюю версию, вы также можете отключить автонастройку окна приема. Если у вас включено объединение сетевых карт, его отключение было бы хорошим вариантом.

Сам пост интересен для чтения, в нем рассказывается об алгоритме повторной попытки соединения TCP/IP. И слава всем людям, которые сказали: «Эй, похоже, это связано с зеркалированием…»! И обратите внимание на комментарий об этом «из-за медленного ответа SQL Server или из-за сетевых задержек».

ФУ!!!

Спасибо всем, кто опубликовал. Теперь мы все должны запросить патч для .NET Framework (или какой-либо другой механизм исправления ADO.NET), поэтому нам не нужно ждать (и покупать) Visual Studio 11...

person ALEXintlsos    schedule 26.05.2011

Тайм-аут соединения отличается от тайм-аута команды. Команда timeout применима к ситуации, когда вы установили соединение, но по каким-то внутренним причинам сервер не может вернуть какие-либо результаты в требуемое время. Время ожидания команды по умолчанию составляет 30 секунд. http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand.commandtimeout.aspx

Попробуйте указать время ожидания подключения в строке подключения. Значение по умолчанию — 15 секунд, что может быть причиной проблемы, которую вы видите. Вы также можете указать время ожидания подключения в коде: http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlconnection.connectiontimeout.aspx

person Piotr Rodak    schedule 15.05.2011
comment
Я думаю об увеличении времени ожидания соединения, но эта ошибка возникает только при подключении, а не при выполнении хранимой процедуры. Время ожидания команды не имеет к этому никакого отношения. Но меня больше беспокоит, почему это происходит только периодически и почему это влияет на так много попыток подключения одновременно. У меня есть администратор базы данных/разработчик, который говорит, что, похоже, что-то происходит с пулом соединений на стороне сервера (и я должен признать, что не знал, что существует такая вещь, как сервер< /i> боковой бассейн). Но это не проверено. - person ALEXintlsos; 24.05.2011
comment
Если с пулом соединений сервера что-то не так, в журнале ошибок SQL Server будут некоторые указания на это. Вы можете это проверить? Я бы подумал, что если сеть будет медленнее в разы, то из-за этого пострадают соединения со всех потоков. Ведь каждое соединение идет по одним и тем же проводам. Недавно у нас была аналогичная проблема - периодические тайм-ауты, испытываемые нашим сервером приложений. Оказалось, что маршрутизация между серверами была неправильной, и производительность сети сильно колебалась. Тогда наше приложение будет генерировать исключения тайм-аута из всех потоков. - person Piotr Rodak; 25.05.2011
comment
Боюсь, в журналах SQL Server ничего нет. Любые рекомендации о том, как обнаружить колебания производительности сети? - person ALEXintlsos; 26.05.2011

Я получаю это время от времени на этом старом сервере базы данных, который у нас есть (сейчас ему 10 лет). Когда это действительно происходит, это потому, что что-то постоянно забивает эту штуку соединениями/запросами. Я предполагаю, что вы обнаружите, что когда это происходит, сервер базы данных находится под нагрузкой (или большое количество подключений или что-то в этом роде). В любом случае, по моему опыту, если вы можете оптимизировать код, оптимизировать базу данных, получить более мощный сервер баз данных и т.д. все помогает. Еще одна вещь, которую вы можете сделать, которую предлагает Петр, - это просто увеличить время ожидания для соединения. Я бы все же прошел и оптимизировал некоторые вещи (должно помочь в долгосрочной перспективе).

person JaCraig    schedule 20.05.2011

Мне удалось несколько надежно воспроизвести эту проблему. У меня есть служба, которая, когда запрашивается задание обработки, запускает обработку в новом домене приложения/потоке. Этот поток будет выполнять от 10 до 16 запросов к базе данных одновременно. Когда я запускаю 30 из этих заданий одно за другим, случайное одно или два задания вылетают с ошибкой тайм-аута.

Я изменил строку подключения, чтобы отключить пул соединений с помощью Pooling = false, а затем ошибка изменилась на следующую. Это вызывается 3 или 4 раза внутри совокупного исключения, поскольку соединения происходят внутри Parallel.For

System.Data.SqlClient.SqlException: Timeout expired.  The timeout period elapsed prior to completion of the operation or the server is not responding.
   at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection)
   at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning()
   at System.Data.SqlClient.TdsParserStateObject.ReadSniError(TdsParserStateObject stateObj, UInt32 error)
   at System.Data.SqlClient.TdsParserStateObject.ReadSni(DbAsyncResult asyncResult, TdsParserStateObject stateObj)
   at System.Data.SqlClient.TdsParserStateObject.ReadNetworkPacket()
   at System.Data.SqlClient.TdsParser.ConsumePreLoginHandshake(Boolean encrypt, Boolean trustServerCert, Boolean& marsCapable)
   at System.Data.SqlClient.TdsParser.Connect(ServerInfo serverInfo, SqlInternalConnectionTds connHandler, Boolean ignoreSniOpenTimeout, Int64 timerExpire, Boolean encrypt, Boolean trustServerCert, Boolean integratedSecurity)
   at System.Data.SqlClient.SqlInternalConnectionTds.AttemptOneLogin(ServerInfo serverInfo, String newPassword, Boolean ignoreSniOpenTimeout, TimeoutTimer timeout, SqlConnection owningObject)
   at System.Data.SqlClient.SqlInternalConnectionTds.LoginWithFailover(Boolean useFailoverHost, ServerInfo primaryServerInfo, String failoverHost, String newPassword, Boolean redirectedUserInstance, SqlConnection owningObject, SqlConnectionString connectionOptions, TimeoutTimer timeout)
   at System.Data.SqlClient.SqlInternalConnectionTds.OpenLoginEnlist(SqlConnection owningObject, TimeoutTimer timeout, SqlConnectionString connectionOptions, String newPassword, Boolean redirectedUserInstance)
   at System.Data.SqlClient.SqlInternalConnectionTds..ctor(DbConnectionPoolIdentity identity, SqlConnectionString connectionOptions, Object providerInfo, String newPassword, SqlConnection owningObject, Boolean redirectedUserInstance)
   at System.Data.SqlClient.SqlConnectionFactory.CreateConnection(DbConnectionOptions options, Object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection)
   at System.Data.ProviderBase.DbConnectionFactory.CreateNonPooledConnection(DbConnection owningConnection, DbConnectionPoolGroup poolGroup)
   at System.Data.ProviderBase.DbConnectionFactory.GetConnection(DbConnection owningConnection)
   at System.Data.ProviderBase.DbConnectionClosed.OpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory)
   at System.Data.SqlClient.SqlConnection.Open()
   at Tps.PowerTools.CoreEngine.V5.DataAccess.DataContext.ExecuteQuery(PtQuery query, ValueStore`1 store, String readerDescription) in C:\SourceCode\Tps.PowerToolsV1\Trunk\Libraries\CoreEngine\CoreEngine.V5\DataAccess\DataContext.cs:line 326
   at Tps.PowerTools.CoreEngine.V5.DataAccess.DataContext.<StockHistoricalData>b__15(PtQuery query) in C:\SourceCode\Tps.PowerToolsV1\Trunk\Libraries\CoreEngine\CoreEngine.V5\DataAccess\DataContext.cs:line 302
   at System.Threading.Tasks.Parallel.<>c__DisplayClass32`2.<PartitionerForEachWorker>b__30()
   at System.Threading.Tasks.Task.InnerInvokeWithArg(Task childTask)
   at System.Threading.Tasks.Task.<>c__DisplayClass7.<ExecuteSelfReplicating>b__6(Object )
person BrandonAGr    schedule 15.05.2011

Оптимизация запросов, которые вы выполняете на удаленном сервере, всегда поможет. Рассчитывайте каждый запрос и ищите длительные. Если вы просто выполняете чтение, используйте (NOLOCK) намек на операторы SELECT. Это было спасением для меня. Просто прочитайте его, чтобы убедиться, что он подходит для вашего приложения. Если у вас есть доступ к удаленной базе данных, убедитесь, что индексы не слишком фрагментированы. Это приведет к значительному замедлению выполнения запроса. Убедитесь, что индексы перестроены/реорганизованы в рамках плана обслуживания SQL. Добавьте новые индексы, где это необходимо.

Увеличение тайм-аута может ухудшить ситуацию. Если вы позволите запросам выполняться дольше, то, возможно, время ожидания большего количества запросов истечет. Тайм-аут предназначен для защиты сервера и других клиентов, обращающихся к нему. Немного увеличить его не так уж сложно, но вы не хотите, чтобы запросы выполнялись в течение длительного времени, убивая сервер.

person nbushnell    schedule 20.05.2011
comment
Я думаю, что этот ответ был бы уместен, если бы у меня возникла проблема во время выполнения команды; однако это происходит при открытии соединения, поэтому запросы еще даже не начались. - person ALEXintlsos; 24.05.2011
comment
Если вы просто выполняете чтение, используйте подсказку (NOLOCK) - это не очень хороший общий совет. - person StingyJack; 02.11.2016
comment
@StingyJack, вот почему есть предостережение. Просто прочитайте его, чтобы убедиться, что оно подходит для вашего приложения. в моем ответе. - person nbushnell; 14.11.2016