2015-10-13 3 views
3

Я бег многопоточной операции, которая извлекает данные из базы данных SQL Server с помощью SqlConnection и Parallel.ForEach() и происходят следующее:ADO.net не закрывает TCP соединения достаточно быстро

  • Я обернуть SqlConnection в using, чтобы соединение было правильно настроено.
  • Моего процесс последовательно бросает SqlException обернутые в AggregateException после успешного запуска в течение некоторого времени («Произошла ошибка сети связанного или экземпляра конкретного при установлении соединения с SQL Server ..»)
  • Я обнаружил, что это происходит около 2^14 (16384) звонков в базу данных (всего, по всем потокам).
  • Я запустил perfmon, и я вижу, что это также количество TCP-соединений, которые открываются прямо около времени, когда генерируется исключение (счетчик «Установленные соединения»).
  • Я уверен, что в моем коде отсутствует утечка связи - там очень мало мест, где я запрашиваю базу данных, и все они правильно распоряжаются соединением (на самом деле, нет другого шаблона, который я использую для запроса в другую базу данных чем ваниль using(...))
  • Я отключил пул соединений, и происходит такое же поведение.
  • Как ни странно, если я удалю индекс в SQL Server, который быстро выполнит мои запросы, операция завершится успешно (хотя и очень медленно) - исключение не выбрасывается. Я заметил, что # из Connections Established линейно растет примерно до 13K, а затем стабилизируется в течение некоторого времени, а затем есть некоторые периоды с линейным спадом, все время операции выполняется.
  • Мое заключение состоит в том, что с установленным индексом .net обрабатывает данные быстрее, чем может закрыть соединения и, в конечном итоге, попадает на какой-то порог сокета ОС или .NET. Без индекса .NET все еще поддерживает слишком много соединений, но у него есть время, чтобы закрыть их достаточно, чтобы порог максимального открытого сокета не попал.

Я в затруднении, как проинструктировать .NET о том, чтобы закрыть эти соединения. Я думал, что это произошло автоматически, когда установлен SqlConnection.

+0

Что особенно странно в том, что когда пул соединений активен с использованием настройки по умолчанию (100 подключений), этот ADO отлично работает с расширением TCP-соединений - как будто объединенные соединения просто заброшены?!?! – SFun28

ответ

1

Проблема заключается в том, как открывается соединение. Я использую этот конструктор

public SqlConnection(string connectionString, SqlCredential credential)

Кажется, что ADO создает новый пул соединений, даже когда connectionString то же самое и credential инкапсулирует те же учетные данные.Я предполагаю, что ADO каким-то образом не может связывать учетные данные с предыдущими вызовами (может быть, это сработает, если бы я использовал один и тот же объект учетных данных с помощью ссылочного равенства? В моем случае я создаю новый SqlCredential с каждым вызовом).

Поскольку каждый пул создается каждый раз, количество воздушных шаров TCP-соединений. Я думаю, что это также имеет смысл, когда пул соединений отключен. Скорее всего, некоторые из настроек сокета или TCP здесь используются (сохранить alives?), И ОС поддерживает открытые соединения для соблюдения этих настроек.

0

Даже несмотря на то, что объекты соединения будут выпущены обратно в пул, неуправляемые ресурсы могут не освобождаться до тех пор, пока не будет выполнена сборка мусора. Путем замедления команд (удаление этого индекса) вы даете GC достаточно времени для освобождения ресурсов обратно в ОС.

Если вы не используете выполнение команды Async, вы можете попытаться кэшировать объекты подключения на уровне потока. Параллельные операции позволят просто повторно использовать одни и те же открытые соединения вместо захвата новых сокетов и т. Д.

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

+1

может быть периодический вызов GC.Collect() будет достаточно, и я мог бы избежать сложности кэширования. – SFun28

+0

Вызов 'GC.Collect', за которым следует' GC.WaitForPendingFinalizers() ', похоже, не помогает. – SFun28

Смежные вопросы