Como posso resolver um problema do pool de conexões entre o ASP.NET e o SQL Server?

211

Nos últimos dias, vemos muito esta mensagem de erro em nosso site:

"O tempo limite expirou. O período de tempo limite transcorreu antes da obtenção de uma conexão do pool. Isso pode ter ocorrido porque todas as conexões do pool estavam em uso e o tamanho máximo do pool foi atingido."

Não mudamos nada em nosso código há algum tempo. Revisei o código para verificar as conexões abertas que não foram fechadas, mas achei que tudo estava bem.

  • Como posso resolver isso?

  • Preciso editar este pool?

  • Como posso editar o número máximo de conexões deste pool?

  • Qual é o valor recomendado para um site de alto tráfego?


Atualizar:

Preciso editar algo no IIS?

Atualizar:

Descobri que o número de conexões ativas está entre 15 e 31 e descobri que o número máximo permitido de conexões configuradas no SQL Server é superior a 3200, é 31 a mais ou devo editar algo na migração de ASP.NET ?

Amr Elgarhy
fonte
O tamanho máximo do pool padrão é 100 se bem me lembro. A maioria dos sites não usa mais de 50 conexões sob carga pesada - depende de quanto tempo suas consultas levam para serem concluídas. Correção de curto prazo na cadeia de conexão: tentar definir um valor mais alto em suas seqüências de conexão: "Max Pool Size = ..."
splattne
quanto? torná-lo 200, por exemplo?
Amr Elgarhy 22/03/09
3
Eu acho que você realmente deve procurar o que está causando o problema. Suas consultas (ou algumas delas) estão demorando muito?
22310 splattne
pode ser isso é a verdadeira razão, uma consulta que está levando a muito tempo para executar, vou procurar em que, graças
Amr Elgarhy
1
Espero que você possa encontrar o problema. Uma sugestão: se você estiver usando SQL Server, experimentar o "SQL Profiler" e olhar para consultas longas: sql-server-performance.com/articles/per/...
splattne

Respostas:

218

Na maioria dos casos, os problemas de pool de conexão estão relacionados a "vazamentos de conexão". Seu aplicativo provavelmente não fecha suas conexões com o banco de dados de maneira correta e consistente. Quando você deixa as conexões abertas, elas permanecem bloqueadas até que o coletor de lixo .NET as feche para você, chamando o Finalize()método delas .

Você quer ter certeza de que está realmente fechando a conexão . Por exemplo, o código a seguir causará um vazamento de conexão, se o código entre .Opene gerar Closeuma exceção:

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

A maneira correta seria esta:

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

ou

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

Quando sua função retorna uma conexão a partir de um método de classe, certifique-se de armazená-la localmente e chamar seu Closemétodo. Você vazará uma conexão usando este código, por exemplo:

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

A conexão retornada da primeira chamada para getConnection()não está sendo fechada. Em vez de fechar sua conexão, essa linha cria uma nova e tenta fechá-la.

Se você usar SqlDataReaderou a OleDbDataReader, feche-os. Mesmo que o fechamento da conexão pareça ser suficiente, faça um esforço extra para fechar explicitamente os objetos do leitor de dados quando você os usar.


Este artigo " Por que um pool de conexões transborda? " Da MSDN / SQL Magazine explica muitos detalhes e sugere algumas estratégias de depuração:

  • Execute sp_whoou sp_who2. Esses procedimentos armazenados do sistema retornam informações da sysprocessestabela do sistema que mostra o status e as informações sobre todos os processos de trabalho. Geralmente, você verá um SPID (Server Process ID) por conexão. Se você nomeou sua conexão usando o argumento Nome do Aplicativo na cadeia de conexão, será fácil encontrar suas conexões de trabalho.
  • Use o SQL Server Profiler com o TSQL_Replaymodelo SQLProfiler para rastrear conexões abertas. Se você conhece o Profiler, esse método é mais fácil do que pesquisar usando sp_who.
  • Use o Monitor de desempenho para monitorar os conjuntos e conexões. Discuto esse método em um momento.
  • Monitore os contadores de desempenho no código. Você pode monitorar a integridade do seu pool de conexões e o número de conexões estabelecidas usando rotinas para extrair os contadores ou usando os novos controles .NET PerformanceCounter.
splattne
fonte
4
Uma pequena correção: o GC nunca chama o método Dispose de um objeto, apenas seu finalizador (se houver). O finalizador pode então fazer uma chamada de "fallback" para Dispose, se necessário, embora não tenha certeza se SqlConnection faz isso.
23909 LukeH
1
Existe algum tipo de degradação do desempenho quando precisamos definir o tamanho máximo do pool 50 e ter apenas alguns usuários.
Mahesh Sharma
37

Ao instalar o .NET Framework v4.6.1, nossas conexões com um banco de dados remoto começaram imediatamente a atingir o tempo limite devido a essa alteração .

Para corrigir, adicione o parâmetro TransparentNetworkIPResolutionna cadeia de conexão e defina-o como false :

Servidor = myServerName; Banco de dados = myDataBase; Trusted_Connection = True; TransparentNetworkIPResolution = False

ajbeaven
fonte
Nesse caso, o problema é "O período de tempo limite decorrido antes da obtenção de uma conexão do pool". Sua correção de cadeia de conexão resolveu isso ou foi um problema de handshake separado?
FBryant87
1
Pelo que me lembro, era exatamente a mesma mensagem de erro da pergunta. Ocorreu imediatamente após a atualização para o .NET Framework v4.6.1.
ajbeaven
Esse também foi o meu caso, corrigi-lo em um Serviço de Aplicativo que eu estava executando no Azure, conexão com um banco de dados SQL do Azure. Eu estava usando o Dapper e descartando corretamente as conexões, mas ainda assim recebi a mensagem de erro "tempo limite decorrido antes de obter uma conexão do pool". Mas não mais, por isso obrigado @ajbeaven
Steve Kennaird
13

A menos que seu uso tenha aumentado muito, parece improvável que exista apenas um acúmulo de trabalho. Na IMO, a opção mais provável é que algo esteja usando conexões e não as liberando imediatamente. Tem certeza de que está usando usingem todos os casos? Ou (através de qualquer mecanismo) liberando as conexões?

Marc Gravell
fonte
13

Você verificou os DataReaders que não estão fechados e o response.redirects antes de fechar a conexão ou um datareader. As conexões permanecem abertas quando você não as fecha antes de um redirecionamento.

Ivo
fonte
3
+1 - Ou funções que retornam DataReaders - Connection vai nunca fecham fora da função que você criou-los ...
splattne
1
Se a sua função retorna SQLDataReader você é melhor para convertê-lo para DataTable que aumentar o tamanho máximo do pool
live-amor
10

Ocasionalmente, também encontramos esse problema em nosso site. O culpado, no nosso caso, são as nossas estatísticas / índices ficarem desatualizados. Isso faz com que uma consulta em execução anteriormente rápida se torne (eventualmente) lenta e com tempo limite.

Tente atualizar as estatísticas e / ou recriar os índices nas tabelas afetadas pela consulta e veja se isso ajuda.

Alex Czarto
fonte
3
Eu acho que isso explicaria por que uma consulta poderia atingir o tempo limite, mas não acho que isso explicaria por que ocorreu um tempo limite ao tentar obter uma conexão.
precisa saber é o seguinte
1
Pode ser que, com um índice incorreto, as consultas demorem mais e mais conexões sejam usadas ao mesmo tempo.
LosManos 29/07
1
Trabalhou para mim depois de atualizar todas as estatísticas com sp_updatestats em um db: EXEC sp_updatestats;
boateng 26/10/15
6

Você pode especificar o tamanho mínimo e máximo do pool especificando MinPoolSize=xyze / ou MaxPoolSize=xyzna cadeia de conexão. Esta causa do problema pode ser uma coisa diferente no entanto.

Mehrdad Afshari
fonte
3
Qual é o MaxPoolSize recomendado?
Amr Elgarhy 22/03/2009
2
Provavelmente, o melhor caminho a seguir é não especificá-lo, a menos que você tenha requisitos especiais e saiba um bom tamanho de piscina para o seu caso específico .
Mehrdad Afshari 22/03/09
1
Nota: verifiquei e descobri que as conexões ativas ao meu db são cerca de 22 ao vivo, isso é demais?
Amr Elgarhy 22/03/2009
Acho que não. O tamanho padrão do pool é de 100 conexões, eu acho. Depende da carga de cada conexão na rede e no servidor SQL. Se estiverem executando consultas pesadas, isso poderá causar problemas. Além disso, podem ocorrer problemas de rede ao iniciar uma nova conexão e causar essa exceção.
Mehrdad Afshari 22/03/09
5

Também encontrei esse problema ao usar alguma camada de dados de terceiros em um dos meus aplicativos .NET. O problema era que a camada não fechava as conexões corretamente.

Jogamos fora a camada e criamos uma, que sempre fecha e descarta as conexões. Desde então, não recebemos mais o erro.

Wim Haanstra
fonte
Estamos usando o LLBL e o site está em funcionamento há 2 anos e apenas nos últimos dias começaram a agir assim.
Amr Elgarhy 22/03/2009
5

Você pode tentar isso também, para resolver o problema de tempo limite:

Se você não adicionou httpRuntime ao seu webconfig, adicione-o na <system.web>tag

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

e

Modifique sua string de conexão como esta;

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

No último uso

    try
    {...} 
    catch
    {...} 
    finaly
    {
     connection.close();
    }
Ka_Ya
fonte
3

Isso ocorre principalmente porque a conexão não foi fechada no aplicativo. Use "MinPoolSize" e "MaxPoolSize" na cadeia de conexão.

subramanian.m
fonte
3

No meu caso, eu não estava fechando o objeto 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.
            }
DevelopZen
fonte
2

Se você estiver trabalhando em um código legado complexo, em que um simples uso de (..) {..} não é possível - como eu era -, você pode conferir o snippet de código que publiquei nesta pergunta do SO para descobrir como pilha de chamadas da criação da conexão quando uma conexão está potencialmente vazando (não fechada após um tempo limite definido). Isso torna bastante fácil identificar a causa dos vazamentos.

LOAS
fonte
2

Não instancie a conexão sql muitas vezes. Abra uma ou duas conexões e use-as para todas as próximas operações sql.

Parece que mesmo Disposenas conexões a exceção é lançada.


fonte
2

Além das soluções publicadas ....

Ao lidar com 1000 páginas de código legado, cada uma chamando um GetRS comum várias vezes, aqui está outra maneira de corrigir o problema:

Em uma DLL comum existente, adicionamos a opção 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);   
    }

Então, em cada página, desde que você feche o leitor de dados, a conexão também será fechada automaticamente para evitar vazamentos.

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

Acabei de ter o mesmo problema e queria compartilhar o que me ajudou a encontrar a fonte: Adicione o nome do aplicativo à sua cadeia de conexão e, em seguida, controle a conexão aberta com o 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>'
Manfred Wippel
fonte
1

Este problema eu tive no meu código. Vou colar algum código de exemplo que encontrei abaixo do erro. O período de tempo limite transcorreu antes da obtenção de uma conexão do pool. Isso pode ter ocorrido porque todas as conexões do pool estavam em uso e o tamanho máximo do pool foi atingido.

 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();**

Você deseja fechar a conexão sempre. Antes disso, eu não nos fazia a conexão estreita devido a isso, recebi um erro. Depois de adicionar a instrução close, vim esse erro

Aravindhan R
fonte
0

Você vazou conexões no seu código. Você pode tentar usar usando para certificar que os está fechando.

 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-pool-and-the-timeout-expired-exception-faq/

EduLopez
fonte
0

Este problema eu encontrei antes. Acabou sendo um problema com o firewall. Acabei de adicionar uma regra ao firewall. Eu tive que abrir a porta 1433para que o servidor SQL pudesse se conectar ao servidor.

Feisal Aswad
fonte
0

Usa isto:

finally
{
    connection.Close();
    connection.Dispose();
    SqlConnection.ClearPool();
}
Jeno M
fonte
12
Talvez eu esteja perdendo o objetivo SqlConnection.ClearPool, mas isso impede que sua conexão atual seja liberada de volta ao pool de conexões? Eu pensei que a idéia do pool de conexões era permitir conexões mais rápidas. Certamente, liberar a conexão da piscina toda vez que terminar significa que uma nova conexão precisará ser criada A TODA Vez que é necessária, em vez de retirar uma sobressalente da piscina? Por favor, explique como e por que essa técnica é útil.
Dib
0

Sim, existe uma maneira de alterar a configuração. Se você estiver em um servidor dedicado e simplesmente precisar de mais conexões SQL, poderá atualizar as entradas "tamanho máximo do pool" nas duas cadeias de conexão seguindo estas instruções:

  1. Entre no seu servidor usando a Área de Trabalho Remota
  2. Abra Meu computador (Windows - E) e vá para C: \ inetpub \ vhosts [domínio] \ httpdocs
  3. Clique duas vezes no arquivo web.config. Isso pode ser listado apenas como web se a estrutura do arquivo estiver configurada para ocultar extensões. Isso abrirá o editor do Visual Basic ou similar.
  4. Encontre suas Strings de conexão, elas serão semelhantes aos exemplos abaixo:

    "add name =" SiteSqlServer "connectionString =" server = (local); database = dbname; uid = dbuser; pwd = dbpassword; pooling = true; tempo de vida da conexão = 120; tamanho máximo do pool = 25; ""

5. Altere o valor tamanho máximo do pool = X para o tamanho necessário do pool.

  1. Salve e feche seu arquivo web.config.
Srusti Thakkar
fonte
0

No meu caso, eu tinha um loop infinito (de uma propriedade get tentando obter valor do banco de dados) que continuava abrindo centenas de conexões SQL.

Para reproduzir o problema, tente o seguinte:

while (true)
{
    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        connection.Open();
        someCall(connection);
    }
}
stomy
fonte