Как я могу решить проблему пула соединений между ASP.NET и SQL Server?


211

Последние несколько дней мы видим это сообщение об ошибке на нашем сайте слишком много:

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

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

  • Как я могу решить это?

  • Нужно ли редактировать этот пул?

  • Как я могу отредактировать максимальное количество соединений в этом пуле?

  • Каково рекомендуемое значение для сайта с высоким трафиком?


Обновить:

Нужно ли что-то редактировать в IIS?

Обновить:

Я обнаружил, что число активных подключений составляет от 15 до 31, и обнаружил, что максимально допустимое количество подключений, настроенных на сервере SQL, превышает 3200, слишком много - 31, или я должен что-то редактировать в конфигурации ASP.NET. ?


Максимальный размер пула по умолчанию равен 100, если я правильно помню. Большинство веб-сайтов не используют более 50 подключений под большой нагрузкой - это зависит от того, сколько времени потребуется для выполнения ваших запросов. Краткосрочное исправление в строке подключения: попробуйте установить более высокое значение в строках подключения: «Максимальный размер пула = ...»
splattne

Сколько? например, 200?
Амр Элгархи

3
Я думаю, что вы должны действительно искать то, что вызывает проблему. Ваши запросы (или некоторые из них) выполняются очень долго?
splattne

может быть, это и есть настоящая причина, запрос, выполнение которого занимает много времени, я буду искать в этом, спасибо
Amr Elgarhy

1
Я надеюсь, что вы можете найти проблему. Совет: если вы используете SQL Server, попробуйте «SQL Profiler» и ищите длинные запросы: sql-server-performance.com/articles/per/…
splattne

Ответы:


218

В большинстве случаев проблемы с пулами соединений связаны с «утечками соединений». Ваше приложение, вероятно, не закрывает свои подключения к базе данных правильно и последовательно. Когда вы оставляете соединения открытыми, они остаются заблокированными, пока сборщик мусора .NET не закроет их для вас, вызвав их Finalize()метод.

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

var connection = new SqlConnection(connectionString);
connection.Open();
// some code
connection.Close();                

Правильный путь будет такой:

var connection = new SqlConnection(ConnectionString);
try
{
     connection.Open();
     someCall (connection);
}
finally
{
     connection.Close();                
}

или

using (SqlConnection connection = new SqlConnection(connectionString))
{
     connection.Open();
     someCall(connection);
}

Когда ваша функция возвращает соединение из метода класса, убедитесь, что вы кэшируете его локально и вызываете его Closeметод. Вы потеряете соединение, используя этот код, например:

var command = new OleDbCommand(someUpdateQuery, getConnection());
result = command.ExecuteNonQuery();
connection().Close(); 

Соединение, возвращенное с первого звонка, getConnection()не закрывается. Вместо того, чтобы закрывать ваше соединение, эта строка создает новое и пытается закрыть его.

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


В этой статье « Почему переполнение пула соединений? » Из журнала MSDN / SQL объясняется много деталей и предлагаются некоторые стратегии отладки:

  • Запустите sp_whoили sp_who2. Эти системные хранимые процедуры возвращают информацию из sysprocessesсистемной таблицы, которая показывает состояние и информацию обо всех рабочих процессах. Как правило, вы увидите один идентификатор процесса сервера (SPID) для каждого соединения. Если вы назвали свое соединение, используя аргумент «Имя приложения» в строке соединения, ваши рабочие соединения будет легко найти.
  • Используйте SQL Server Profiler с TSQL_Replayшаблоном SQLProfiler для отслеживания открытых соединений. Если вы знакомы с Profiler, этот метод проще, чем опрос с использованием sp_who.
  • Используйте системный монитор для мониторинга пулов и соединений. Я обсуждаю этот метод через минуту.
  • Мониторинг счетчиков производительности в коде. Вы можете отслеживать состояние вашего пула соединений и количество установленных соединений, используя процедуры извлечения счетчиков или используя новые элементы управления .NET PerformanceCounter.

4
Небольшое исправление: сборщик мусора никогда не вызывает метод Dispose объекта, только его финализатор (если он есть). Финализатор может затем выполнить «запасной» вызов Dispose, если это необходимо, хотя я не уверен, что SqlConnection делает это.
LukeH

1
Есть ли какое-либо снижение производительности, когда нам нужно установить максимальный размер пула 50, а у нас всего несколько пользователей.
Махеш Шарма

37

После установки .NET Framework v4.6.1 наши подключения к удаленной базе данных сразу же начали истекать из- за этого изменения .

Для исправления просто добавьте параметр TransparentNetworkIPResolutionв строку подключения и установите для него значение false :

Server = MyServerName; Database = MyDatabase; Trusted_Connection = True; TransparentNetworkIPResolution = False


В этом случае возникает проблема «Время ожидания истекло до получения соединения из пула». Исправило ли это исправление в строке подключения, или это была отдельная проблема с установкой соединения?
FBryant87

1
Из того, что я помню, это было точно такое же сообщение об ошибке, как в вопросе. Это произошло сразу после обновления до .NET Framework v4.6.1.
ajbeaven

Это также относится ко мне, исправил это для меня в службе приложений, которую я запускал в Azure, подключился к базе данных SQL Azure. Я использовал Dapper и правильно удалял соединения, но все еще получал сообщение об ошибке «истекло время ожидания до получения соединения из пула». Но не более, так что спасибо @ajbeaven
Стив Кеннард

13

Если ваше использование не увеличилось, кажется маловероятным, что есть просто отставание в работе. ИМО, наиболее вероятным вариантом является то, что что-то использует соединения и не освобождает их быстро. Вы уверены, что используете usingво всех случаях? Или (через какой-либо механизм) освободить соединения?


13

Проверяли ли вы DataReaders, которые не закрыты, и response.redirects перед закрытием соединения или устройства чтения данных. Соединения остаются открытыми, когда вы не закрываете их перед перенаправлением.


3
+1 - Или функции, возвращающие DataReaders - Соединение никогда не закроется вне функции, которую вы их создали ...
splattne

1
Если ваша функция возвращает SqlDataReader, вам лучше преобразовать ее в DataTable, чем увеличив максимальный размер пула
live-love

10

Мы также время от времени сталкиваемся с этой проблемой на нашем веб-сайте. В нашем случае виновником является наша статистика / индексы, устаревшие. Это приводит к тому, что ранее быстро выполняющийся запрос (в конце концов) замедляется и время ожидания истекает.

Попробуйте обновить статистику и / или перестроить индексы в таблицах, затронутых запросом, и посмотрите, поможет ли это.


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

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

1
Работал у меня после обновления всей статистики с помощью sp_updatestats на БД: EXEC sp_updatestats;
Боатенг

6

Вы можете указать минимальный и максимальный размер пула, указав MinPoolSize=xyzи / или MaxPoolSize=xyzв строке подключения. Эта причина проблемы могла быть другой вещью как бы то ни было.


3
Какой рекомендуемый MaxPoolSize?
Амр Элгархи

2
Вероятно, лучший способ - не указывать его, если у вас нет особых требований и вы не знаете подходящий размер пула для вашего конкретного случая .
Мердад Афшари

1
Примечание: я проверил, и я обнаружил, что активные соединения с моей базой данных около 22 живых, это слишком много?
Амр Элгархи

Я так не думаю. Я думаю, что размер пула по умолчанию составляет 100 соединений. Это зависит от загрузки каждого соединения в сети и на сервере SQL. Если они выполняют тяжелые запросы, это может вызвать проблемы. Кроме того, могут возникнуть проблемы с сетью при инициализации нового соединения, что может вызвать это исключение.
Мердад Афшари

5

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

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


Мы используем LLBL, и веб-сайт работает с 2 лет, и только последние несколько дней начали действовать так.
Амр Элгархи

5

Вы также можете попробовать это для решения проблемы тайм-аута:

Если вы не добавили httpRuntime в веб-конфигурацию, добавьте это в <system.web>тег

<sytem.web>
     <httpRuntime maxRequestLength="20000" executionTimeout="999999"/>
</system.web>

и

Измените строку подключения следующим образом;

 <add name="connstring" connectionString="Data Source=DSourceName;Initial Catalog=DBName;Integrated Security=True;Max Pool Size=50000;Pooling=True;" providerName="System.Data.SqlClient" />

При последнем использовании

    try
    {...} 
    catch
    {...} 
    finaly
    {
     connection.close();
    }

3

В основном это связано с тем, что соединение не было закрыто в приложении. Используйте «MinPoolSize» и «MaxPoolSize» в строке подключения.


3

В моем случае я не закрывал объект DataReader.

        using (SqlCommand dbCmd = new SqlCommand("*StoredProcedureName*"))
        using (dbCmd.Connection = new SqlConnection(WebConfigurationAccess.ConnectionString))
            {
            dbCmd.CommandType = CommandType.StoredProcedure;

            //Add parametres
            dbCmd.Parameters.Add(new SqlParameter("@ID", SqlDbType.Int)).Value = ID;
.....
.....
            dbCmd.Connection.Open();
            var dr = dbCmd.ExecuteReader(); //created a Data reader here
            dr.Close();    //gotta close the data reader
            //dbCmd.Connection.Close(); //don't need this as 'using' statement should take care of this in its implicit dispose method.
            }

2

Если вы работаете над сложным унаследованным кодом, где простое использование (..) {..} невозможно - как я и сделал - вы можете проверить фрагмент кода, который я разместил в этом вопросе SO, чтобы определить стек вызовов создания соединения, когда соединение потенциально утечек (не закрывается после установленного времени ожидания). Это позволяет довольно легко определить причину утечки.


2

Не создавайте SQL-соединение слишком много раз. Откройте одно или два соединения и используйте их для всех последующих операций sql.

Кажется, что даже при Disposeподключении возникает исключение.


2

В дополнение к размещенным решениям ....

Имея дело с 1000 страницами устаревшего кода, каждая из которых вызывает общий GetRS несколько раз, вот еще один способ решить эту проблему:

В существующей общей DLL мы добавили опцию CommandBehavior.CloseConnection :

    static public IDataReader GetRS(String Sql)
    {
        SqlConnection dbconn = new SqlConnection(DB.GetDBConn());
        dbconn.Open();
        SqlCommand cmd = new SqlCommand(Sql, dbconn);
        return cmd.ExecuteReader(CommandBehavior.CloseConnection);   
    }

Затем на каждой странице, пока вы закрываете устройство чтения данных, соединение также автоматически закрывается, поэтому утечки соединения предотвращаются.

    IDataReader rs = CommonDLL.GetRS("select * from table");
    while (rs.Read())
    {
        // do something
    }
    rs.Close();   // this also closes the connection

2

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

select st.text,
    es.*, 
    ec.*
from sys.dm_exec_sessions as es
    inner join sys.dm_exec_connections as ec on es.session_id = ec.session_id
    cross apply sys.dm_exec_sql_text(ec.most_recent_sql_handle) st
where es.program_name = '<your app name here>'

1

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

 String query = "insert into STATION2(ID,CITY,STATE,LAT_N,LONG_W) values('" + a1 + "','" + b1 + "','" + c1 + "','" + d1 + "','" + f1 + "')";
    //,'" + d1 + "','" + f1 + "','" + g1 + "'

    SqlConnection con = new SqlConnection(mycon);
    con.Open();
    SqlCommand cmd = new SqlCommand();
    cmd.CommandText = query;
    cmd.Connection = con;
    cmd.ExecuteNonQuery();
    **con.Close();**

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


0

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

 Using (SqlConnection sqlconnection1 = new SqlConnection(“Server=.\\SQLEXPRESS ;Integrated security=sspi;connection timeout=5”)) {
                          sqlconnection1.Open();
                          SqlCommand sqlcommand1 = sqlconnection1.CreateCommand();
                          sqlcommand1.CommandText = raiserror (‘This is a fake exception’, 17,1)”;
                          sqlcommand1.ExecuteNonQuery();  //this throws a SqlException every time it is called.
                          sqlconnection1.Close(); //Still never gets called.
              } // Here sqlconnection1.Dispose is _guaranteed_

https://blogs.msdn.microsoft.com/angelsb/2004/08/25/connection-pooling-and-the-timeout-expired-exception-faq/


0

С этой проблемой я сталкивался раньше. В итоге возникла проблема с брандмауэром. Я просто добавил правило в брандмауэр. Мне пришлось открыть порт, 1433чтобы сервер SQL мог подключиться к серверу.


0

Использовать это:

finally
{
    connection.Close();
    connection.Dispose();
    SqlConnection.ClearPool();
}

12
Может быть, я упускаю суть SqlConnection.ClearPool, но разве это не позволяет вернуть ваше текущее соединение обратно в пул соединений? Я думал, что идея пула соединений состояла в том, чтобы позволить более быстрые соединения. Надежное освобождение соединения из пула каждый раз, когда оно завершается, означает, что НОВОЕ соединение нужно будет создавать КАЖДЫЙ РАЗ, а не вытаскивать запасное из пула? Пожалуйста, объясните, как и почему эта техника полезна.
Диб

0

Да, есть способ изменить конфигурацию. Если вы находитесь на выделенном сервере и вам просто нужно больше соединений SQL, вы можете обновить записи «max pool size» в обеих строках соединения, следуя этим инструкциям:

  1. Войдите в свой сервер с помощью удаленного рабочего стола
  2. Откройте Мой компьютер (Windows - E) и перейдите в C: \ inetpub \ vhosts [домен] \ httpdocs
  3. Дважды щелкните файл web.config. Это может быть просто указано как веб, если структура файла настроена на скрытие расширений. Это откроет Visual Basic или похожий редактор.
  4. Найдите строки подключения, они будут похожи на примеры ниже:

    "add name =" SiteSqlServer "connectionString =" server = (local); database = dbname; uid = dbuser; pwd = dbpassword; pooling = true; время жизни соединения = 120; максимальный размер пула = 25; ""

5. Измените максимальный размер пула = X на требуемый размер пула.

  1. Сохраните и закройте файл web.config.

0

Убедитесь, что вы установили правильные настройки для пула соединений. Это очень важно, как я объяснил в следующей статье: https://medium.com/@dewanwaqas/configurations-that-significantly-improves-your-app-performance-built-using-sql-server-and-net- ed044e53b60 Вы увидите значительное улучшение производительности вашего приложения, если вы последуете ему.


0

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

Чтобы воспроизвести проблему, попробуйте это:

while (true)
{
    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        connection.Open();
        someCall(connection);
    }
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.