Управление подключением к базе данных с помощью ADO.NET - PullRequest
2 голосов
/ 05 декабря 2008

У нас есть приложение, построенное на ADO.NET. Мы следуем некоторым простым рекомендациям, которые позволяют нам использовать пул соединений. Например, блок кода, использующий базу данных, может выглядеть примерно так:

using( DbConnection dbConnection = GetDatabaseConnection() ) {   
    doWork();
}

FWIW, в GetDatabaseConnection нет ничего особенного. Он устанавливает соединение с базой данных, на которой работает MSSQL Server 2000. На самом деле он выглядит следующим образом:

DbConnection GetDatabaseConnection() {
    return GetConnection(MyConnectionString);
}

DbConnection GetConnection(String connectionString)
{
  try {
      SqlConnection dbConnection = new SqlConnection(connectionString);
      dbConnection.Open();
      return dbConnection;
  } catch( InvalidOperationException ex ) {
      handleError(ex);
      throw;
  } catch( DbException ex ) {
      handleError(ex);
  }
}

Таким образом, наши соединения удаляются в конце области блока. Однако при тестировании приложения мы столкнулись с небольшим затруднением. Мы обнаружили, что наше приложение очень бурное, то есть бывают моменты, когда оно становится очень болтливым, а затем на некоторое время замолкает. В результате у нас может быть несколько потоков, которые будут одновременно подключаться для получения соединения.

Итак, представьте, что у вас есть 10 потоков. Партия работы (пожалуйста, не пытайтесь перефразировать партию работы) прибывает и разбита на несколько потоков. Затем каждый поток пытается получить соединение и бум, я получаю исключение InvalidOperationException. Я настроил ConnectTimeout, и все, что он делает, это растягивает время, пока я не получу пакет исключений. Как только я закончу фазу «запуска», приложение будет в порядке. Затем он снова успокаивается, соединения «уходят» и процесс начинается снова.

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

  • Постоянно поддерживать некоторое соединение "горячим"
  • Попытка снова открыть соединение до нескольких попыток
  • Реализация собственного пула соединений (бле, не заинтересован в изобретении колеса)

Edit:

Большинство форумов, которые я прочитал, не одобряют увеличение размера пула соединений. По умолчанию пул соединений имеет верхнюю границу 50 соединений (этого более чем достаточно - если мне придется увеличить его, в другом месте что-то принципиально не так). Что я заметил, так это то, что когда ConnectTimeout имеет низкий уровень, возникает исключение InvalidOperationException. Это как если бы раскрутка соединения была слишком продолжительной, а время ожидания ожидающих соединений истекло.

MARS, безусловно, вариант ... Текст InvalidOperationException.Message:

Истекло время ожидания. Время ожидания истекло до получения соединения из пула. Это могло произойти из-за того, что все пулы подключений использовались и был достигнут максимальный размер пула.

Ответы [ 4 ]

2 голосов
/ 05 декабря 2008

Из MSDN (http://msdn.microsoft.com/en-us/library/8xx3tyca.aspx):

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

Диспетчер соединений удовлетворяет запросы на соединения, перераспределяя соединения, когда они возвращаются обратно в пул. Если достигнут максимальный размер пула и доступное соединение недоступно, запрос помещается в очередь. Затем диспетчер пытается восстановить любые подключения до истечения времени ожидания (по умолчанию 15 секунд). Если диспетчер не может удовлетворить запрос до истечения времени ожидания соединения, выдается исключение .

Перевод: Проверьте контексты транзакции ... если у вас размер пула 10 подключений, а в разных транзакциях создано 10 подключений, вы облажались.

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

Если соединение с пропавшим сервером существует, это соединение можно извлечь из пула, даже если диспетчер соединений не обнаружил разорванное соединение и пометил его как недействительное. Это так, потому что накладные расходы на проверку того, что соединение все еще действует, устранят преимущества наличия пула, вызвав еще одну обратную передачу на сервер. Когда это происходит, при первой попытке использовать соединение обнаружит, что соединение разорвано, и выдается исключение .

Перевод: Неужели нельзя полагаться на соединение, которое нужно подключить? Статья не объясняет, как справиться с этим ...

Вы можете иногда пытаться очистить пул вручную, используя ClearAllPools и ClearPool, но для меня это звучит как пластырь, заставляет меня съеживаться.

В статье также обсуждаются контексты безопасности:
После активации роли приложения SQL Server путем вызова системной хранимой процедуры sp_setapprole, контекст безопасности этого соединения не может быть сброшен. Однако если пул включен, соединение возвращается в пул, и возникает ошибка при повторном использовании пула.

Я начинаю задумываться, почему я использую пул соединений ...

И наконец:
Фрагментация пула благодаря встроенной защите
Соединения объединяются в соответствии со строкой соединения и идентификацией пользователя. Поэтому, если вы используете обычную проверку подлинности или проверку подлинности Windows на веб-сайте и вход в систему с интегрированной защитой, вы получаете один пул на пользователя. Хотя это повышает производительность последующих запросов к базе данных для одного пользователя, этот пользователь не может использовать преимущества подключений других пользователей. Это также приводит как минимум к одному соединению на пользователя с сервером базы данных.

Так что, если вы используете встроенную защиту в веб-приложении, вы можете пополнить свой пул соединений, если у вас достаточно пользователей.

Не зная больше подробностей о вашем приложении, трудно увеличить масштаб того, что может сбить вас с толку, но, надеюсь, это даст вам некоторые идеи, где искать.

НТН

0 голосов
/ 05 декабря 2008

Вопрос 1. Ваш метод GetDatabaseConnection () выводит новый поток для генерации соединения с базой данных и возвращает его в основной поток ... или несколько потоков пытаются получить доступ к этому единственному варианту метода ... или этот метод общий / статический метод, который просто захватывает соединение из пула?

Вопрос 2: Марка и модель вашего сервера БД? SQL Server? Oracle? PostgreSQL?

Вопрос 3: Требуется ли вам вся работа, выполненная на разных соединениях, или, если бы все это можно было сделать на одном, этого было бы достаточно? Если SQL Server, то, возможно, MARS, разрешающий использование нескольких наборов записей в одном соединении, может помочь, но может оказаться невозможным для вашего приложения.

Вопрос 4. Что такое деталь в возвращенном исключении?

Просто пытаюсь получить четкое представление о том, как работает архитектура вашего приложения ...

(П.С. Кто-нибудь знает, как я помечаю «ответ» как «не ответ, а просьбу о дополнительных разъяснениях» ... т. Е. Для этого «ответа»)

0 голосов
/ 05 декабря 2008

Согласно MSDN, InvalidOperationException вызывается SqlConnection.Open, если вы не указали источник данных или сервер, или если соединение уже открыто.

Вы уверены, что у вас нет вызовов SqlConnection.Open в вашем методе "doWork"?

Также ваш код в методе GetConnection поглощает DbException.

0 голосов
/ 05 декабря 2008

Вы можете попробовать установить «Максимальный размер пула» выше. Также вы можете попробовать явно вызвать «Close» для соединения.

...