Перейти к содержимому

Pool size что это

  • автор:

Объединение подключений в пул в SQL Server (ADO.NET)

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

На практике большинство приложений использует только одно или несколько различных конфигураций соединений. Это означает, что во время выполнения приложения многие идентичные соединения будут повторно открываться и закрываться. Чтобы свести к минимуму затраты на открытие подключений, ADO.NET использует метод оптимизации, называемый пулом подключений.

Пул соединений снижает количество открытий новых соединений. Владелец пула поддерживает управление физическим подключением. Он управляет соединениями с помощью поддержания набора активных соединений для каждой конфигурации данного соединения. Каждый раз, когда пользователь вызывает метод Open в соединении, организатор пулов ищет в пуле доступное соединение. Если соединение пула доступно, вместо открытия нового соединения он возвращает его участнику. При вызове приложением метода Close в соединении вместо закрытия организатор пулов возвращает его в набор активных соединений пула. После возвращения соединения в пул оно готово к повторному использованию при следующем вызове метода Open .

В пул могут помещаться только соединения с одинаковой конфигурацией. ADO.NET хранит несколько пулов одновременно, по одному для каждой конфигурации. Соединения разделяются в пулы строкой соединения, а при использовании встроенной безопасности — удостоверением Windows. Соединения также заносятся в пул в зависимости от того, прикреплены ли они к транзакции. При использовании метода ChangePassword экземпляр SqlCredential влияет на пул соединений. Различные экземпляры SqlCredential используют различные пулы соединений, даже если идентификатор пользователя и пароль совпадают.

Организация пулов соединений может существенно улучшить производительность и масштабируемость приложения. По умолчанию пул подключений включен в ADO.NET. Пока организатор пулов не будет явно отключен, он оптимизирует соединения по мере их открытия и закрытия в приложении. Также можно указать несколько модификаторов строки соединения для управления поведением пула соединений. Дополнительные сведения см. в подразделе «Управление пулами соединений с помощью ключевых слов строки соединения» далее в этом разделе.

Если включено создание пулов соединения и происходит ошибка времени ожидания или другая ошибка входа, то выдается исключение и последующие попытки подключения завершаются ошибкой в течение следующего «интервала блокирования» — 5 секунд. Если приложение пытается установить подключение в течение интервала блокирования, то снова выдается первое исключение. Последующие ошибки после завершения интервала блокирования приводят к возникновению новых интервалов блокирования, вдвое превышающих предыдущий период времени блокирования, вплоть до максимального значения в 1 минуту.

Создание и назначение пулов

При первом открытии соединения создается пул соединений, основанный на алгоритме точного совпадения, связанного с пулом строкой соединения. Каждый пул соединений связывается с отдельной строкой соединения. При открытии нового соединения, если строка соединения не соответствует в точности существующему пулу, создается новый пул. Соединения заносятся в пул отдельно для процесса, домена приложения, строки соединения и, если используется встроенная безопасность, для удостоверения Windows. Строки соединения должны точно совпадать. Ключевые слова, указанные в различном порядке для одного соединения, будут включены в пул по отдельности.

В следующем примере C# создаются три новых объекта SqlConnection, но для управления ими требуется только два пула соединений. Обратите внимание, что первая и вторая строки соединения отличаются значениями, присвоенными аргументу Initial Catalog .

using (SqlConnection connection = new SqlConnection( "Integrated Security=SSPI;Initial Catalog=Northwind")) < connection.Open(); // Pool A is created. >using (SqlConnection connection = new SqlConnection( "Integrated Security=SSPI;Initial Catalog=pubs")) < connection.Open(); // Pool B is created because the connection strings differ. >using (SqlConnection connection = new SqlConnection( "Integrated Security=SSPI;Initial Catalog=Northwind")) < connection.Open(); // The connection string matches pool A. >

Если аргумент Min Pool Size не указан в строке соединения или указано значение 0, соединения в пуле будут закрыты после периода отсутствия активности. Однако, если значение аргумента Min Pool Size больше 0, пул соединений не уничтожается, пока не будет выгружен домен приложения AppDomain и не завершится процесс. Обслуживание неактивных или пустых пулов требует минимальных системных издержек.

Пул автоматически очищается при возникновении неустранимой ошибки, например переходе на другой ресурс.

Добавление соединений

Пул соединений создается для каждой уникальной строки соединения. При создании пула создается множество объектов соединения, которые добавляются в пул для удовлетворения требования к минимальному размеру пула. Соединения добавляются в пул по необходимости, вплоть до указанного максимального размера пула (по умолчанию — 100). Соединения освобождаются обратно в пул при закрытии или ликвидации.

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

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

Настоятельно рекомендуется всегда закрывать соединение после его использования, чтобы оно вернулось в пул. Это можно сделать с помощью методов Close или Dispose объекта Connection , либо открывая все подключения внутри инструкции using (C#) или Using (Visual Basic). Соединения, которые явно не закрыты, нельзя добавить или вернуть в пул. Дополнительные сведения см. в разделе Оператор using или Практическое руководство. Удаление системного ресурса для Visual Basic.

В методе Close вашего класса нельзя вызывать методы Dispose или Connection объектов DataReader , Finalize или любого другого управляемого объекта. В методе завершения следует только освобождать неуправляемые ресурсы, которыми ваш класс непосредственно владеет. Если класс не владеет какими-либо неуправляемыми ресурсами, не включайте в его определение метод Finalize . Дополнительные сведения см. в статье Сборка мусора.

Дополнительные сведения о событиях, связанных с открытием и закрытием подключений, вы найдете в разделах документации по SQL Server: Audit Login, класс событий и Audit Logout, класс событий.

Удаление соединений

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

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

Очистка пула

ADO.NET 2.0 появились два новых метода очистки пула: ClearAllPools и ClearPool. Метод ClearAllPools очищает пулы соединений данного поставщика, а метод ClearPool — пул, связанный с конкретным соединением. Если во время вызова методов соединения использовались, они соответствующим образом помечаются. При их закрытии вместо возвращения в пул они удаляются.

Поддержка транзакций

Соединения выбираются из пула и назначаются в зависимости от контекста транзакции. Если в строке соединения не указан аргумент Enlist=false , пул соединений гарантирует прикрепление соединения к контексту Current. После закрытия и возврата соединения в пул с прикрепленной транзакцией System.Transactions оно резервируется таким образом, что следующий запрос к пулу соединений с той же транзакцией System.Transactions вернет это соединение, если оно доступно. Если при выдаче такого запроса в пуле нет доступных соединений, соединение берется и прикрепляется из части пула, не использующей транзакции. Если доступных соединений нет во всех частях пула, создается и прикрепляется новое соединение.

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

Управление пулами соединений с помощью ключевых слов строки соединения

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

Фрагментация пула

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

Фрагментация пула из-за встроенной безопасности

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

Фрагментация пула из-за многочисленных баз данных

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

Это также является побочным эффектом конструкции приложений. При соединении с SQL Server существует относительно простой способ устранения этого побочного эффекта без нарушения безопасности. Вместо соединения каждого пользователя или группы с отдельной базой данных устанавливается соединение с одной базой данных на сервере, а затем выполняется инструкция Transact-SQL USE, чтобы переключиться на требуемую базу данных. Следующий фрагмент кода демонстрирует создание первоначального соединения с базой данных master и последующее переключение на требуемую базу данных, указанную в строковой переменной databaseName .

' Assumes that command is a valid SqlCommand object and that ' connectionString connects to master. command.Text = "USE DatabaseName" Using connection As New SqlConnection(connectionString) connection.Open() command.ExecuteNonQuery() End Using 
// Assumes that command is a SqlCommand object and that // connectionString connects to master. command.Text = "USE DatabaseName"; using (SqlConnection connection = new SqlConnection( connectionString))

Роли приложений и пул соединений

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

Альтернативы ролям приложений

Рекомендуется пользоваться преимуществом новых механизмов безопасности, которые пришли на смену ролям приложения. Дополнительные сведения см. в разделе Создание ролей приложения в SQL Server.

См. также

  • Объединение подключений в пул
  • SQL Server и ADO.NET
  • Счетчики производительности
  • Общие сведения об ADO.NET

Pool size что это

Осуществлние подключения к базе данных представляет довольно затратную с точки зрения времени операцию, посколько в процессе подключения системе надо выполнить кучу шагов: парсинг строки подключения, проверка подключения сервером и т.д. При этом в программе, как правило, используются по несколько раз одни и те же конфигурации подключения (которые имеют одну и ту же строку подключения). И чтобы оптимизировать процессе подключения, в ADO.NET используется механизм пула подключений .

Пул подключений позволяет использовать ранее созданные подключения. Так, если параметр Pooling в строке подключения равен true (по умолчанию он равен true), то после закрытия подключения с помощью метода Close()/CloseAsync() закрытое подключение возвращается в пул подключений, где оно оно готово к повторному использованию при следующем вызове метода Open()/OpenAsync() . Когда менеджер подключений, который управляет пулом, получает запрос на открытие нового подключения с помощью метода Open()/OpenAsync() , то он проверяет все подключения пула.

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

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

using Microsoft.Data.SqlClient; using System; using System.Threading.Tasks; namespace HelloApp < class Program < static async Task Main(string[] args) < using(SqlConnection connection = new SqlConnection(connectionString)) < await connection.OpenAsync(); Console.WriteLine(connection.ClientConnectionId); >using (SqlConnection connection = new SqlConnection(connectionString)) < await connection.OpenAsync(); Console.WriteLine(connection.ClientConnectionId); >Console.WriteLine("Программа завершила работу."); Console.Read(); > > >

Например, в моем случае консольный вывод будет следующим:

950d8a37-50a8-44e3-8778-ade70e5415d6 950d8a37-50a8-44e3-8778-ade70e5415d6 Программа завершила работу.

В пул помещаются подключения только с одинаковой конфигурацией. ADO.NET поддерживает несколько пулов одновременно, и для каждой конфигурации строки подключения создается свой собственный пул.

Все подключения в пуле различаются по нескольким признакам:

  • строка подключения
  • учетные записи, используемые при подключении
  • процесс приложения

В следующем примере в первом и третьем блоках using будет использоваться одно и то же подключение из пула, поскольку строка подключения будет совпадать:

using Microsoft.Data.SqlClient; using System; using System.Threading.Tasks; namespace HelloApp < class Program < static async Task Main(string[] args) < string connectionString = "Server=(localdb)\\mssqllocaldb;Database=master;Trusted_Connection=True;"; string connectionString2 = "Server=(localdb)\\mssqllocaldb;Database=tempdb;Trusted_Connection=True;"; using (SqlConnection connection = new SqlConnection(connectionString)) < await connection.OpenAsync(); Console.WriteLine(connection.ClientConnectionId); >using (SqlConnection connection = new SqlConnection(connectionString2)) < await connection.OpenAsync(); Console.WriteLine(connection.ClientConnectionId); >using (SqlConnection connection = new SqlConnection(connectionString)) < await connection.OpenAsync(); Console.WriteLine(connection.ClientConnectionId); >Console.WriteLine("Программа завершила работу."); Console.Read(); > > >

Консольный вывод в моем случае будет следующим:

5df81b79-6229-4ba6-bb46-1671229cb4c1 8c509b49-62ac-4731-892d-16e3a68f5b7e 5df81b79-6229-4ba6-bb46-1671229cb4c1 Программа завершила работу.

Управление пулом

Стоит отметить, что подключение добавляется в пул, если параметр Pooling равен true . Хотя, выше в примерах в строках подключенияя этот параметр не указан, но по умолчанию он равен true . Однако, присвоив значение false , мы можем отключить пуллинг подключения:

using Microsoft.Data.SqlClient; using System; using System.Threading.Tasks; namespace HelloApp < class Program < static async Task Main(string[] args) < string connectionString = "Server=(localdb)\\mssqllocaldb;Database=master;Trusted_Connection=True;"; string connectionString2 = "Server=(localdb)\\mssqllocaldb;Database=tempdb;Trusted_Connection=True;"; using (SqlConnection connection = new SqlConnection(connectionString)) < await connection.OpenAsync(); Console.WriteLine(connection.ClientConnectionId); >using (SqlConnection connection = new SqlConnection(connectionString2)) < await connection.OpenAsync(); Console.WriteLine(connection.ClientConnectionId); >using (SqlConnection connection = new SqlConnection(connectionString)) < await connection.OpenAsync(); Console.WriteLine(connection.ClientConnectionId); >Console.WriteLine("Программа завершила работу."); Console.Read(); > > >

В этом случае, несмотря на одну и ту же строку подключения идентификаторы подключения не будут совпадать:

b750fdba-5570-47a1-ac84-471c3cc57813 b0ce7a14-3cb8-48c3-b0a7-31c7a2fbc090 Программа завершила работу.

Кроме того, мы можем в строке подключения указать параметры Max Pool Size и Min Pool Size .

Max Pool Size указывает на максимальное количество подключений, которое может быть добавлено в пул. Может принимать числовое значение начиная с 1, но не мньше значения параметра Min Pool Size . По умолчанию равен 100.

Min Pool Size указывает на минимально допустимое количество подключений, которое может быть добавлено в пул. Может принимать числовое значение начиная с 0, но не больше значения из параметра Max Pool Size . По умолчанию равен 0.

Если параметр Min Pool Size не указан в строке подключения или его значение равно 0, то подключения в пуле будут закрыты после периода отсутствия активности (4-8 минут), либо если разорвана связь с сервером базы данных. Но если значение параметра Min Pool Size больше 0, пул подключений не удаляется, пока не завершится процесс приложения.

Кроме того, класс SqlConnection имеет два статических метода для очистки пула:

  • SqlConnection.ClearPool(connection) : удаляет из пула конкретное подключение SqlConnection, которое передается в качестве параметра
  • SqlConnection.ClearAllPools() : полностью очищает пул подключений

Например, в следующем примере создаются два отдельных подключения, поскольку в первом случае подключение удаляется из пула:

using Microsoft.Data.SqlClient; using System; using System.Threading.Tasks; namespace HelloApp < class Program < static async Task Main(string[] args) < string connectionString = "Server=(localdb)\\mssqllocaldb;Database=master;Trusted_Connection=True;"; using (SqlConnection connection = new SqlConnection(connectionString)) < await connection.OpenAsync(); Console.WriteLine(connection.ClientConnectionId); SqlConnection.ClearPool(connection); >//SqlConnection.ClearAllPools(); using (SqlConnection connection = new SqlConnection(connectionString)) < await connection.OpenAsync(); Console.WriteLine(connection.ClientConnectionId); >Console.WriteLine("Программа завершила работу."); Console.Read(); > > >

Записная книжка программиста-новичка, C#, SQL, PHP и все-все-все

Я ведь это уже делал, но хрен теперь найдешь тот кусок кода, гуглим снова… Где бы найти простое и понятное руководство для начинающего, а не тонкости для мега-гуру?

Главная→C#→ Пул соединений с базой данных в ADO.NET / OleDb — как избежать проблем с утечкой соединений в ASP.NET (перевод)

Рубрики

Свежие записи

  • Вырезаем числовую часть из начала строки в transact-sql
  • Пул соединений с базой данных в ADO.NET / OleDb — как избежать проблем с утечкой соединений в ASP.NET (перевод)
  • ASP .Net MVC, JQuery и AJAX — отсылаем данные на сервер
  • Разделитель тысяч и дробной части для decimal
  • Создаем расширенный Control в WinForms наследуясь от существующего
  • Вставка строк и изменение границ ячеек в Excel Interop из C#
  • Как прочитать данные из удаленного DataRow в DataTable
  • Проблемы с кодировкой при копировании русского текста из MS SQL Studio в Outlook/Word/Office
  • Как проверить, существует ли таблица в MS SQL Server перед удалением/созданием
  • Очень просто.
  • Получаем выбранные строки DataGridView, в котором выбраны только ячейки
  • Ошибка в коде привела к убыткам в 476 миллионов долларов и банкротству компании
  • Отслеживаем изменения выбранного значения в колонке ComboBox DataGridView (DataGridViewComboBoxColumn)
  • Excel 2010, Windows 7, два монитора и «ошибка при направлении команды приложению»
  • Удаляем default-ограничение (constraint) в Transact Sql

Свежие комментарии

  • Kirill к записи Самоучитель по C# для начинающих. 01. Основы языка, переменные, логика, циклы.
  • как избавиться от чувства вины к записи Как добавить строку/текст в начало файла в C# и .Net
  • DannyLef к записи Полезные расширения-плагины для WordPress
  • как избавиться от чувства вины к записи Как добавить строку/текст в начало файла в C# и .Net
  • gweg2ehgwEHERWQHQ к записи Простейшее диалоговое окно-вопрос (MessageBox) в WindowsForms

Архивы

Мета

Пул соединений с базой данных в ADO.NET / OleDb — как избежать проблем с утечкой соединений в ASP.NET (перевод)

Опубликовано 13.10.2014 автором Ведомир

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

Работа пула соединения регулируется параметрами в строке соединения. Следующие четыре параметра контролируют большую часть поведения пула соединений:

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

— Max Pool Size — определяет максимальный размер пула соединений. По умолчанию 100. Большинство веб-сайтов используют не более 40 одновременных соединений под самой тяжелой нагрузкой, но это зависит от того, сколько времени занимает выполененние ваших запросов к базе

— Min Pool Size — начальное количестов соединений, который будут добавлены в пул после его создания. По умолчанию ноль, но вы можете заменить его небольшим числом, если вашему приложению требуется постоянно время ответа даже после многочасового простоя. В таком случае пользователю не придется ждать открытия этих соединений.

— Pooling — включает и выключает использование пула. Как вы наверно догадываетесь по умолчанию true. Ниже я опишу те ситуации, в которых вы можете использовать Pooling=false.

Распространенные проблемы и их решения

Проблемы с пулом соединений почти всегда вызваны «утечкой соединений» — ситуацией, когда ваше приложение не закрывает свои соединения с базой данных. «Утекшие» соединения остаются открытыми, пока сборщик мусора не закроет их за вас, вызвав метод Dispose. В отличие от старого ADO, ADO.NET требует от вас вручную закрывать соединения сразу после того, как вы закончите с ними работать. Если вы рассчитываете на выход объектов соединений за пределы видимости, подумайте еще раз. Могут пройти часы до того, как сборщик мусора займется ими. В это время ваше приложение может уже зависнуть встречая пользователей чем-то вроде:

Exception: System.InvalidOperationException Message: Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached. Source: System.Data at System.Data.SqlClient.SqlConnectionPoolManager.GetPooledConnection(SqlConnectionString options, Boolean& isInTransaction) at System.Data.SqlClient.SqlConnection.Open() . Exception: System.InvalidOperationException Message: Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached. Source: System.Data at System.Data. SqlClient.SqlConnectionPoolManager.GetPooledConnection ( SqlConnectionString options, Boolean& isInTransaction) at System.Data.SqlClient.SqlConnection.Open()

Закрывайте свои соединения

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

SqlConnection conn = new SqlConnection(myConnectionString); conn.Open(); doSomething(); conn.Close();

Если doSomething() выбросил исключение, соединение никогда не будет правильно закрыто. Это можно исправить вот так:

SqlConnection conn = new SqlConnection(myConnectionString); try < conn.Open(); doSomething(conn); >finally
using (SqlConnection conn = new SqlConnection(myConnectionString))

Вы заметили, что в первом случае мы явно вызвали conn.Close(), а во втором мы заставили компиллятор сгенерировать неявный вызов conn.Dispose() сразу после завершения блока using? Блок using в C# гарантирует, что на его обьекте будет вызван метод Dispose сразу после завершения этого блока. Для обьекта Connection методы Close и Dispose равнозначны. Ни один из них не имеет каких-то особых преимуществ.

Создавая обьект соединения методом класса убедитесь, что вы сохранили его локально и вызвали метод Close. Следующий код приведет к утечке соединений:

OleDbCommand cmd new OleDbCommand(myUpdateQuery, getConnection()); intres = cmd.ExecuteNonQuery(); getConnection().Close(); // Полученное при первом вызове метода getConnection() соединение не было закрыто, сейчас мы создаем новое соединение и пытаемся закрыть уже его

Если вы используете SqlDataReader, OleDbDataReader и т.п., закройте их. Даже если закрытие соединения должно закрывать и их, приложите дополнительные усилия для закрытия ваших объектов чтения данных после использования.

Последнее по порядку, но не по важности, никогда не вызывайте Close или Dispose вашего соединения в деструкторе класс или вашем методе Finalize. Это не только бессмысленно в плане закрытия соединений, но и может вызывать ошибки из-за вмешательства в работу сборщика мусора. Больше информации здесь.
http://msdn.microsoft.com/library/en-us/cpguide/html/cpconprogrammingessentialsforgarbagecollection.asp.

Тестируем ваши изменения

Единственный способ узнать эффект ваших изменений в работе с пулом соединений — это провести нагрузочное тестирование вашего приложения. Если у вас уже есть юнит-тесты — используйте их. Циклическое воспроизведение юнит-тестов создаст хорошую нагрузку на ваше приложение. Если у вас их нет, использует инструмент для нагрузочного тестирования веб-приложений. Есть масса коммерческих инструментов для нагрузочного тестирования. Если вы предпочитаете бесплатный софт, можете попробовать OpenSTA. После установки вы сможете прокликать в нем ваше приложение и сохранить это как сценарий нагрузочного теста.

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

Отслеживание поведения пула соединений

Обычно вам просто надо знать, что приложение не выходит за рамки пула соединений. Если количество соединений постепенно растет после начального периода «разогрева» при неизменной нагрузке, то скорее всего вы столкнулись с утечкой соединений. Простейши способ отслеживать количество соединений с базой данных — это использование Performance Monitor в Administrative tools на большинстве версий Windows. Если вы используете MS SQl Server, добавьте счетчик SQL Server General Statistics -> User Connections (он доступен на компьютере с SQL Server, так что возможно вам понадобится добавить ее имя или IP-адрес в поле Select Counters From Computer). Кроме этого можно использовать запросы к вашей базе. Для SQL Server выполнете:

SELECT * FROM V$SESSION WHERE PROGRAM IS NOT NULL

Счетчики производительности .NET CLR Data performance counters

В документации вы могли прочитать про счетчики производительности .Net CLR. Они великолепны, ечли вы знаете пределы их возможностей. Помните, что они не всегда правильно сбрасываются. Слдующая статься проливает немного света на эту проблему, но по моему мнению не покрывает все возможные случаи. http://support.microsoft.com/default.aspx?scid=kb;en-us;314429. Так же стоит помнить о том, чьл IIS выгружаем домены приложений под нагрузкой, так что не удивляйтесь, если ваше количество соединений станет нулевым, даже если минимальный размер пула 5!

Краткосрочные исправления

Что, если вы обнаружили утечку соединений на рабочей версии не можете остановить ее до выяснения подробностей? Отключите пул. Пусть производительность приложения сильно упадет, но оно будет работать! Потребление памяти тоже вырастет. Но что, если приложение падает не так уж и часто и вы не хотите жертвовать производительностью? Попробуйте следующее:

conn = new SqlConnection();
try
conn.ConnectionString ; // Обратите внимание, что таймаут всего две секунды!
conn.Open();
>
catch(Exception)
if (conn.State != ConnectionState.Closed) conn.Close();
conn.ConnectionString = «integrated security=SSPI;SERVER=YOUR_SERVER;DATABASE = YOUR_DB_NAME;Pooling=false;Connect Timeout=45;»;
conn.Open();
>

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

Комментарии

Пул соединений с базой данных в ADO.NET / OleDb — как избежать проблем с утечкой соединений в ASP.NET (перевод) — Комментарии (8)

Спасибо! То что было нужно! Хороший блог! Почему уже не пишешь? И где форма подписки на обновления в блоге?!

Ведомир говорит 17.11.2014 в 13:15 :

Стас:
Спасибо! То что было нужно! Хороший блог! Почему уже не пишешь? И где форма подписки на обновления в блоге?!

Спасибо за отзыв — разные бывают в жизни обстоятельства в том числе со здоровьем связанные. Стандартный RSS-поток всегда здесь, хотя заметную кнопку надо будет прикрутить — у меня давно спецкнопка для RSS-потоков новостей вынесена на главную панель инструментов Firefox, даже странно подумать что может быть иначе. А еще есть мои перевода на Хабрахабре и Geektimes

Александр говорит 07.05.2016 в 5:22 :

Здравствуйте, есть очень интересные статьи, но не могли бы вы подсказать статьи про разработку сетевых приложений?
socet — ы, http запросы и т.п Очень хочется , а информации об этом мало

Самир говорит 03.06.2016 в 11:52 :

Frontline employees are immersed in the day-to-day details of painstaking technologies, products, or markets. No everybody is more finished in the realities of a troop’s doings than they are. But while these employees are deluged with extraordinarily clear-cut report, they commonly learn it bloody baffling to turn that knowledge into usable knowledge. For one fetich, signals from the marketplace can be expressionless and ambiguous. On another, employees can skirt so caught up in their own confining angle that they conquered spectacle of the broader context. The more holistic way to cognition at multitudinous Japanese companies is also founded on another axiom insight. A companions is not a device but a living organism. Much like an distinct, it can pass on birth to a collective apology of uniqueness and necessary purpose. This is the organizational compeer of self-knowledge—a shared skilfulness of what the company stands pro the aid of, where it is prosperous, what kind of circle it wants to live in, and, most notable, how to get that eat a reality. Nonaka and Takeuchi are arguing that creating wisdom will grow the indication to sustaining a competitive utility in the future. Because the competitive air and living soul preferences changes constantly, knowledge perishes quickly. With The Knowledge-Creating Attendance, managers be struck by means of at their fingertips years of perspicacity from Japanese firms that fling how to set up conception continuously, and how to utilize it to skip town prospering unheard of products, services, and systems .

This is very fascinating, You are a very professional blogger.
I have joined your feed and look forward to in search of extra of your magnificent post.
Also, I have shared your site in my social networks

Пул соединений MySQL и как это можно использовать для распараллеливания

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

Занимаюсь фрилансом. Как-то свел меня один мой знакомый с владельцами автошколы и там меня попросили сделать программу для формирования своей коллекции билетов. Что-то вроде своей книги билетов ПДД. Все вопросы хранятся в СУБД MySQL в трех таблицах, где в одной коллекция вопросов, в другой тематика этих вопросов и в последней ответы на вопросы. В результате 800 вопросов с картинкой или без нее, включая ответы. Первоначальный вариант формирования представлял собой обычный метод с последовательным формированием вопросов внутри. Мне довольно интересна тема многопоточного выполнения программы поэтому после создания вполне рабочего метода, я решил сделать все более удобным, а заодно прибавил скорости выборки.

Начнем с того, что для формирования своей версии билетов ПДД необходимо учитывать тематику вопросов. При этом всего 12 тем. Первые 8 тем состоят из 40 вопросов, оставшиеся из 120. Каждой теме соответствует определенный номер в билете, при этом первым 8 темам соответствует 1 вопрос из билета, другим 3 вопроса из билета. Для хранения сгенерированной версии вопросов используется словарь Dictionary, где каждый ключ хранит в себе список вопросов по определенной тематике. При этом порядок вопросов должен быть всегда разным, т.е. необходим метод, который будет генерировать последовательность чисел от 0 до 40 без повторений, так получится выбирать любой билет и соответственно вопрос из него. С учетом всего получается такой алгоритм формирования всех вопросов:

  • получаем все темы, которые у нас есть;
  • из тем формируем список номеров вопросов для каждой из тем;
  • генерируем случайные числа;
  • на основе списка вопросов и последовательности случайных чисел делаем выборку и формируем список вопросов по тематике;
  • заносим каждый список в словарь.

Подключение к БД

Рассмотрим подключение к БД. Для этого используем абстрактный класс, который будет хранить в себе строку соединения, Connection и DataReader:

public abstract class SqlBase < protected static readonly String Connect; protected readonly MySqlConnection SqlConnection; protected MySqlDataReader SqlDataReader; static SqlBase() < Connect = String.Format("Database=;Data Source=;User Settings.Default.DataSource, Settings.Default.UserId, Settings.Default.Password); > protected SqlBase() < try < this.SqlConnection = new MySqlConnection(Connect); this.SqlConnection.Open(); >catch (Exception ex) < throw new Exception(ex.Message, ex); >> > 

Мне нравятся абстрактные классы тем, что это они позволяют создавать и скрывать в себе удобную логику работы, позволяя в производном полностью сосредоточиться на другой конкретике и не беспокоится о других деталях, возложив обязанности на базовый класс. В данном случае создавая экземпляр производного класса, мы автоматически получаем необходимые условия для работы с БД и нам остается реализовать логику самого класса для формирования выборки вопросов:

public sealed class SqlPerceptionQuestions : SqlBase < public Dictionary> GetQuestion() < Generation(); GetTheme(); return Request(); >private Dictionary> Request() < var _collectionDictionary = new Dictionary>(); for(int ctr = 0; ctr < 12; ctr++) < using (var _questions = new SqlQuestions()) < if (ctr < 8) < _collectionDictionary[ctr] = _questions.GetQuestionSmall((Int16)ctr); >else < _collectionDictionary[ctr] = _questions.GetQuestionGreat((Int16)ctr); >> return _collectionDictionary; > private async void GetTheme() < //логика метода >//метод для получения данных для темы с 40 вопросами private List GetQuestionSmall(Int16 numTheme) < var _listQuestions = new List(); for (Int16 numCard = 0; numCard < 40; numCard++) < _listQuestions.Add(GetQuestion(numCard, numTheme)); >return _listQuestions; > //метод для получения данных для темы со 120 вопросами private List GetQuestionGreat(Int16 numTheme) < var _listQuestions = new List(); for (Int16 numQuestion = 0; numQuestion < 3; numQuestion++) for (int numCard = 0; numCard < 40; numCard++) < _listQuestions.Add(GetQuestion(numQuestion, numTheme, numQuestion)); >return _listQuestions; > // метод для получения одного вопроса из БД по параметрам private Question GetQuestion(Int16 numCard, Int16 numTheme, Int16 numQuestion = 0) < //логика метода >//получение ответа на вопрос private List GetResponse(Int32 questions_id) < //логика метода >> 

Это самая простая и синхронная версия, которая работает около 2 секунд и 200-400 миллисекунд, что соответственно будет блокировать пользовательский интерфейс на все это время. Это уже неплохо работающая версия, так как самая первая реализация работала довольно долго, около 6 секунд. После улучшения вышло всего около 2 секунд.

Создание асинхронной версии выборки

Все хорошо и все уже работает, но так ли, как должно быть? Ведь у нас синхронный метод(блокирующий) и не консольное приложение. Необходима правильная и вполне работающая программа, которая не будет блокироваться даже на половину секунды, а будет исправно работать при любой нагрузке. Для этого первым делом перепишем метод GetQuestion(). Сделаем его асинхронным в соответствии с паттерном TAP(Task-based Asynchronous Pattern). Кому интересно почитайте в интернете или есть довольно хорошая книга, которая мне очень нравится — «Асинхронное программирование в C# 5.0» Алекс Дэвис, где очень хорошо описывается данная тематика, либо загляните сюда. Перепишем его и он будет выглядеть так:

public async Task>> GetQuestionAsync() < return await Task.Factory.StartNew(() =>< Generation(); GetTheme(); return Request(); >, TaskCreationOptions.LongRunning); > 

Рассмотрим самое интересное в этом методе: Task.Factory.StartNew(). Начиная с версии .NET 4.5 можно использовать версию Task.Run(), которая отличается от предыдущей более простым объявлением с меньшим числом параметров при создании. По сути Task.Run() она представляет собой просто более удобную оболочку над Task.Factory.StartNew() и очень подходит для простого создания асинхронных задач, но при этом обладает немного меньшей гибкостью управления. Если необходим более точный контроль над тем, какой поток производит вычисления или как он планируется, используем Task.Factory.StartNew(). Если интересно загляните сюда. В этом случае, я использовал этот вариант по причине того, что я также указал такой параметр, как TaskCreationOptions.LongRunning, что помечает данную задачу как длительную и означает, что данный рабочий элемент будет выполняться на протяжении длительного периода времени и может заблокировать другие рабочие элементы. Также данный флаг предоставляет сведения для TaskScheduler, что следует ожидать избыточной подписки и это позволит создать больше потоков, чем количество доступных аппаратных потоков. С помощью этого параметра можно полностью избежать ThreadPool, включая глобальные и локальные очереди. Подробнее читайте «Планировщик заданий».

Таким образом мы получаем уже асинхронное выполнение формирования коллекции данных и не блокируем основной поток.

Распараллеливание выборки

После этого переходим к распараллеливанию, что даст нам прирост по скорости выборки данных. Но для этого придется переделать немного основной класс, изменить базовый и добавить еще один для обеспечения параллельности работы цикла, чтобы избежать гонок данных, критических секций и проблем с переменными для разных потоков.
Для начала создадим новый вложенный класс в классе SqlPerceptionQuestionsSqlQuestions. В него перенесем методы для получения вопросов и ответов на них, а также сделаем его производным от SqlBase, при этом во внешнем классе оставим метод для получения тем, его мы будем вызывать только один раз, а также формирование последовательности чисел.

Код класса SqlQuestions:

 internal sealed class SqlQuestions : SqlBase < internal ListGetQuestionSmall(Int16 numTheme) < var _listQuestions = new List(); for (Int16 numCard = 0; numCard < 40; numCard++) < _listQuestions.Add(GetQuestion(numCard, numTheme)); >return _listQuestions; > internal List GetQuestionGreat(Int16 numTheme) < var _listQuestions = new List(); for (Int16 numQuestion = 0; numQuestion < 3; numQuestion++) for (int numCard = 0; numCard < 40; numCard++) < _listQuestions.Add(GetQuestion(numQuestion, numTheme, numQuestion)); >return _listQuestions; > private Question GetQuestion(Int16 numCard, Int16 numTheme, Int16 numQuestion = 0) < //логика метода >private List GetResponse(Int32 questions_id) < //логика метода >> 

Для распараллеливания цикла будем использовать Parallel.For(). Это довольно удобный способ организовать загрузку данных в несколько потоков. Но это чревато также тем, что нам необходимо будет создать как минимум несколько соединений с БД, так как один Connection способен обрабатывать один DataReader. Перепишем метод .Request():

private Dictionary> Request() < var _collectionDictionary = new Dictionary>(); var _po = new ParallelOptions < MaxDegreeOfParallelism = Environment.ProcessorCount >; Parallel.For(0, 12, _po, ctr => < using (var _questions = new SqlQuestions()) < if (ctr < 8) < _collectionDictionary[ctr] = _questions.GetQuestionSmall((Int16)ctr); >else < _collectionDictionary[ctr] = _questions.GetQuestionGreat((Int16)ctr); >> >); return _collectionDictionary; > 

А после открытия соединения его необходимо закрыть. При этом все это будет происходить в цикле. Для реализации всего этого я и решил создать отдельный производный класс SqlQuestions. Для закрытия соединения будем вызывать .Dispose(), в котором пропишем что нам необходимо делать при закрытии. Для этого сначала объявим метод .Dispose() в базовом классе:

 public void Dispose() < Dispose(true); GC.SuppressFinalize(this); >protected abstract void Dispose(Boolean disposing); 

А реализовывать его будем по-разному в производных классах. Почему так? Начнем с того, что же происходит при создании подключения к БД и ее закрытии? Если открыть соединение, а затем его закрыть, то оно на некоторое время (около 3-х минут) помещается в пул соединений MySQL, и если открыть новое, то соединение берется из пула, что означает что время и ресурсы на повторное открытие не используются. Давайте запустим наши новые методы и посмотрим сколько времени уходит на открытие соединений БД, для этого вставим в базовом классе Stopwatch в код, где происходит открытие соединения и посмотрим что у нас на выходе. Код:

protected SqlBase() < Stopwatch st = new Stopwatch(); st.Start(); try < this.SqlConnection = new MySqlConnection(Connect); this.SqlConnection.Open(); >catch (MySqlException ex) < throw new Exception(ex.Message, ex); >st.Stop(); Debug.WriteLine("Время открытия соединения БД : " + st.Elapsed.Seconds.ToString() + " секунд " + st.Elapsed.Milliseconds.ToString() + " миллисекунд"); > 

Время открытия

image

Первое соединение самое долгое, здесь открывается соединение для класса SqlPerceptionQuestions, которое будет открыто на все время работы метода. Последующие соединения — это те, которые были открыты при в цикле при создании экземпляров класса SqlQuestions. С учетом кол-ва процессоров у меня на компьютере, которых 4 получаем, что в цикле максимум будет открыто 4 соединения. Получаем, что всего изначально будет открыто 5 соединений, при этом в цикле будут как открываться, так и закрываться. Поэтому первые 5 соединений требуют времени на открытие, а после когда в цикле будут закрываться старые соединения и открываться новые, то на них не будет уходить время и ресурсы, так как соединения есть в пуле и они просто выдаются каждый раз, когда требуются. Из-за этого и реализуется немного по-разному очистка классов. В классе SqlPerceptionQuestions метод будет выглядеть так:

protected override void Dispose(bool disposing) < if (!this.disposed) < if (SqlDataReader != null) < SqlDataReader.Close(); SqlDataReader.Dispose(); >SqlConnection.Close(); SqlConnection.Dispose(); MySqlConnection.ClearAllPools(); > disposed = true; > 

А в классе SqlQuestions точно также, за исключением строки MySqlConnection.ClearAllPools(); Ведь, если мы ее оставим, то получим такую вот ситуацию:

Время открытия

image

Как мы видим, постоянная очистка пула потоков приводит к постоянному открытию соединения с вытекающими последствиями.

Пул соединений MySql

Рассмотрим немного подробнее этот момент. Connector/Net MySql поддерживает пул соединений для повышения производительности и масштабируемости баз данных приложений с интенсивным использованием. Данная функция включена по умолчанию. Ее можно отключить или изменить характеристики с помощью опций строки подключения. Строка подключения поддерживает опции относительно пула соединений:

Подробности
Cache Server Properties, CacheServerProperties Отключено Определяет, обновлять ли параметры некоторых системных переменных(SHOW VARIABLES) каждый раз, когда соединение из пула возвращается. Включение этой опции ускоряет соединение с контекстом пула соединений. Вашему приложению не сообщается об изменениях конфигурации, сделанных другими соединениями. Данная опция добавлена начиная с версии Connector/Net 6.3.
Connection Lifetime, ConnectionLifeTime 0 Когда соединение возвращается в пул, время его создания сравнивается с текущим временем и, если оно превышает значение ConnectionLifeTime, то данное соединение уничтожается. Это полезно в кластерных конфигурациях для осуществления балансировки нагрузки между рабочим сервером и сервером, находящимся в онлайне. Значение zero(0) заставляет соединения в пуле находится в режиме ожидания максимально возможное время.
Connection Reset, ConnectionReset false Если истина, состояние соединения при извлечении из пула сбрасывается. Значение по умолчанию позволяет избежать дополнительного цикла обработки сервером для получения соединения, но при этом состояние соединения не сбрасывается.
Maximum Pool Size, Max Pool Size, MaximumPoolsize, maxpoolsize 100 Максимальное число соединений, которым дозволено находиться в пуле.
Minimum Pool Size, Min Pool Size, MinimumPoolSize, minpoolsize 0 Минимальное число соединений, которым дозволено находиться в пуле.
Pooling true Если истина, то объект MySqlConnection берется из пула, если необходимо, он создается и добавляется в соответствующий пул. Определенные значения: true, false, yes и no.
  • Connection Reset сбрасывает контекст соединения и если его использовать не по умолчанию, то время на получение соединения из пула незначительно увеличивается.
  • Minimum Pool Size позволяет установить в случае необходимости количество соединений в пуле, которые будут существовать неограниченно длительное время, если количество соединений меньше или равно значению Minimum Pool Size.
  • Отключение Pooling приведет к таким же результатам, если все время очищать пул потоков (как в примере данной программы)

Параллельный стек вызовов

Ради интереса, если поставить точку остановы, например, в методе .GetQuestion() и посмотреть параллельный стек вызовов, увидим:

Параллельный стек

image

Как видно из скриншота, мы находимся в одном из потоков, который приостановлен, а по стеку вызовов определяем, что данный метод был вызван из метода, который загружает малую коллекцию вопросов (40). Слева от него расположены еще 3 потока, причем два из них остановлены в этот момент на строке добавления вопроса в коллекцию, которые также обрабатывают малую коллекцию вопросов. И последний из них, 4 поток занимается обработкой и получает ответ в этот момент на вопрос, только уже для вопроса из большой коллекции (120). Все эти 4 потока были созданы в параллельном цикле и работают практически одновременно с остальными потоками итераций цикла. Эти потоки входят в общее число потоков в программе, которых 8, где оставшиеся 4 решают другие задачи программы.

Последний штрих — обработка исключений

И, наконец, для работы программы нам необходима обработка исключений. Вдруг с БД изменились поля или какие-то еще параметры или в самой программе возникла непредвиденная ошибка. Перепишем метод GetQuestionAsync():

public async Task>> GetQuestionAsync() < return await Task.Factory.StartNew(() => < try < Generation(); GetTheme(); return Request(); >catch (AggregateException ex) < throw new AggregateException(ex.Message, ex.InnerExceptions); >catch (Exception ex) < throw new AggregateException(ex.Message, ex.InnerException); >>, TaskCreationOptions.LongRunning); > 

Обработка исключений AggregateException связана с тем, что цикл Parallel.For, если возникнет исключение, возбудит ошибку такого типа и, следовательно, ее необходимо обработать и передать вызывающей стороне. Вполне логично, что параллельный цикл генерирует ошибку такого рода. Рассмотрим данный момент поподробнее: для этого я изменил Sql-запрос в .GetQuestion(), заведомо неправильно указав один из параметров, который в таблице БД не существует. Получаем:

Ошибка

image

При этом если продолжить отладку, то всего данное исключение возникнет 4 раза, что вполне логично. Для того, чтобы обработать все 4, пусть и относящиеся к одной причине, необходимо как-то их расположить, для чего исключение AggregateException подходит.

Обработка Exception связана с тем, что если возникнет исключение в методе .GetTheme(), то там возникнет одно исключение и его также надо перехватить.

Вызывающий код устроен так:

private async void Button_Click(object sender, RoutedEventArgs e) < Stopwatch st = new Stopwatch(); st.Start(); try < SqlQuest = new SqlPerceptionQuestions(); collectionQuest = await SqlQuest.GetQuestionAsync(); >catch (AggregateException ex) < ex.ShowError(); >catch(Exception ex) < ex.ShowError(); >finally < if (SqlQuest != null) SqlQuest.Dispose(); >st.Stop(); Debug.WriteLine("Время выполнения метода: " + st.Elapsed.Seconds.ToString() + " секунд " + st.Elapsed.Milliseconds.ToString() + " миллисекунд"); Debugger.Break(); > 

Напоследок.

Вообще началось это с того, что когда я писал параллельную версию цикла, я подумал, а как сильно влияет частое открытие и закрытие соединения с БД. Посещая форумы и расспрашивая умных людей я узнал про пул соединений. Потом задался вопросом, а необходимо ли его создавать или он создается в неявном виде? Немного поэкспериментировал и почитав документацию MySQL, в результате пришел к выводу, что очень уж удачная это вещь, пул соединений!

Весь код после изменений

//Базовый класс public abstract class SqlBase : IDisposable < protected static readonly String Connect; protected readonly MySqlConnection SqlConnection; protected MySqlDataReader SqlDataReader; protected Boolean disposed; static SqlBase() < Connect = String.Format("Database=;Data Source=;User Settings.Default.DataSource, Settings.Default.UserId, Settings.Default.Password); > protected SqlBase() < Stopwatch st = new Stopwatch(); st.Start(); try < this.SqlConnection = new MySqlConnection(Connect); this.SqlConnection.Open(); >catch (Exception ex) < throw new Exception(ex.Message, ex); >st.Stop(); Debug.WriteLine("Время открытия соединения БД : " + st.Elapsed.Seconds.ToString() + " секунд " + st.Elapsed.Milliseconds.ToString() + " миллисекунд"); > ~SqlBase() < Dispose(false); >public void Dispose() < Dispose(true); GC.SuppressFinalize(this); >protected abstract void Dispose(Boolean disposing); > //класс для выполнения запросов выборки public sealed class SqlPerceptionQuestions : SqlBase < public async Task>> GetQuestionAsync() < return await Task.Factory.StartNew(() => < try < Generation(); GetTheme(); return Request(); >catch (AggregateException ex) < throw new AggregateException(ex.Message, ex.InnerExceptions); >catch (Exception ex) < throw new AggregateException(ex.Message, ex.InnerException); >>, TaskCreationOptions.LongRunning); > private Dictionary> Request() < var _collectionDictionary = new Dictionary>(); var _po = new ParallelOptions < MaxDegreeOfParallelism = Environment.ProcessorCount >; Parallel.For(0, 12, _po, ctr => < using (var _questions = new SqlQuestions()) < if (ctr < 8) < _collectionDictionary[ctr] = _questions.GetQuestionSmall((Int16)ctr); >else < _collectionDictionary[ctr] = _questions.GetQuestionGreat((Int16)ctr); >> >); return _collectionDictionary; > private void GetTheme() < >private void Generation() < >protected override void Dispose(bool disposing) < if (!this.disposed) < if (SqlDataReader != null) < SqlDataReader.Close(); SqlDataReader.Dispose(); >SqlConnection.Close(); SqlConnection.Dispose(); MySqlConnection.ClearAllPools(); > disposed = true; > //вложенный класс для осуществления удобного формирования параллельных запросов internal sealed class SqlQuestions : SqlBase < internal ListGetQuestionSmall(Int16 numTheme) < var _listQuestions = new List(); for (Int16 numCard = 0; numCard < 40; numCard++) < _listQuestions.Add(GetQuestion(numCard, numTheme)); >return _listQuestions; > internal List GetQuestionGreat(Int16 numTheme) < var _listQuestions = new List(); for (Int16 numQuestion = 0; numQuestion < 3; numQuestion++) for (int numCard = 0; numCard < 40; numCard++) < _listQuestions.Add(GetQuestion(numQuestion, numTheme, numQuestion)); >return _listQuestions; > private Question GetQuestion(Int16 numCard, Int16 numTheme, Int16 numQuestion = 0) < >private List GetResponse(Int32 questions_id) < >protected override void Dispose(bool disposing) < if (!this.disposed) < if (SqlDataReader != null) < SqlDataReader.Close(); SqlDataReader.Dispose(); >SqlConnection.Close(); SqlConnection.Dispose(); > disposed = true; > > > 

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *