¿Cómo puedo resolver un problema de grupo de conexión entre ASP.NET y SQL Server?

211

Los últimos días vemos demasiado este mensaje de error en nuestro sitio web:

"El tiempo de espera expiró. El período de tiempo de espera transcurrió antes de obtener una conexión del grupo. Esto puede haber ocurrido porque todas las conexiones agrupadas estaban en uso y se alcanzó el tamaño máximo del grupo".

No hemos cambiado nada en nuestro código en mucho tiempo. Revisé el código para verificar las conexiones abiertas que no se cerraron, pero encontré que todo estaba bien.

  • ¿Como puedo resolver esto?

  • ¿Necesito editar este grupo?

  • ¿Cómo puedo editar el número máximo de conexiones de este grupo?

  • ¿Cuál es el valor recomendado para un sitio web de alto tráfico?


Actualizar:

¿Necesito editar algo en IIS?

Actualizar:

Descubrí que el número de conexiones activas oscila entre 15 y 31, y descubrí que el número máximo permitido de conexiones configuradas en el servidor SQL es más de 3200 conexiones, son 31 demasiadas o debería editar algo en la configuración de ASP.NET ?

Amr Elgarhy
fuente
El valor predeterminado de Tamaño máximo de agrupación es 100 si no recuerdo mal. La mayoría de los sitios web no usan más de 50 conexiones bajo una carga pesada, depende del tiempo que tarden en completarse sus consultas. Solución a corto plazo en la cadena de conexión: intente establecer un valor más alto en las cadenas de conexión: "Tamaño máximo de
agrupación
¿Cuánto cuesta? hacer 200 por ejemplo?
Amr Elgarhy
3
Creo que realmente deberías buscar lo que está causando el problema. ¿Sus consultas (o algunas de ellas) duran mucho?
splattne
puede ser esa la verdadera razón, una consulta que lleva mucho tiempo ejecutar,
buscaré
1
Espero que puedas encontrar el problema. Una sugerencia: si está utilizando SQL Server, pruebe el "SQL Profiler" y busque consultas largas: sql-server-performance.com/articles/per/…
splattne

Respuestas:

218

En la mayoría de los casos, los problemas de agrupación de conexiones están relacionados con "fugas de conexión". Es probable que su aplicación no cierre sus conexiones a la base de datos de manera correcta y consistente. Cuando deja las conexiones abiertas, permanecen bloqueadas hasta que el recolector de basura .NET las cierra llamando a su Finalize()método.

Desea asegurarse de que realmente está cerrando la conexión . Por ejemplo, el siguiente código provocará una pérdida de conexión, si el código entre .Openy Closearroja una excepción:

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

La forma correcta sería esta:

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

o

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

Cuando su función devuelve una conexión desde un método de clase, asegúrese de guardarla en caché localmente y llamar a su Closemétodo. Perderá una conexión usando este código, por ejemplo:

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

La conexión devuelta desde la primera llamada a getConnection()no se está cerrando. En lugar de cerrar su conexión, esta línea crea una nueva e intenta cerrarla.

Si usa SqlDataReadero a OleDbDataReader, ciérrelos. Aunque cerrar la conexión en sí parece hacer el truco, haga un esfuerzo adicional para cerrar los objetos del lector de datos explícitamente cuando los use.


Este artículo " ¿Por qué se desborda un grupo de conexiones? " De MSDN / SQL Magazine explica muchos detalles y sugiere algunas estrategias de depuración:

  • Corre sp_whoo sp_who2. Estos procedimientos almacenados del sistema devuelven información de la sysprocessestabla del sistema que muestra el estado y la información sobre todos los procesos de trabajo. En general, verá una ID de proceso del servidor (SPID) por conexión. Si nombró su conexión utilizando el argumento Nombre de aplicación en la cadena de conexión, sus conexiones de trabajo serán fáciles de encontrar.
  • Use SQL Server Profiler con la TSQL_Replayplantilla SQLProfiler para rastrear conexiones abiertas. Si está familiarizado con Profiler, este método es más fácil que sondear utilizando sp_who.
  • Use el Monitor de rendimiento para controlar los grupos y las conexiones. Discuto este método en un momento.
  • Monitoree los contadores de rendimiento en el código. Puede supervisar el estado de su grupo de conexiones y la cantidad de conexiones establecidas mediante el uso de rutinas para extraer los contadores o mediante los nuevos controles .NET PerformanceCounter.
splattne
fuente
44
Una pequeña corrección: el GC nunca llama al método Dispose de un objeto, solo a su finalizador (si hay uno). El finalizador puede hacer una llamada "alternativa" para Eliminar, si es necesario, aunque no estoy seguro de si SqlConnection lo hace.
LukeH
1
¿Hay algún tipo de degradación del rendimiento cuando tenemos que configurar el tamaño máximo de la agrupación 50 y tenemos pocos usuarios?
mahesh sharma
37

Al instalar .NET Framework v4.6.1, nuestras conexiones a una base de datos remota comenzaron a expirar inmediatamente debido a este cambio .

Para solucionarlo, simplemente agregue el parámetro TransparentNetworkIPResolutionen la cadena de conexión y configúrelo en falso :

Servidor = myServerName; Base de datos = myDataBase; Trusted_Connection = True; TransparentNetworkIPResolution = False

ajbeaven
fuente
En este caso, el problema es "El tiempo de espera transcurrido antes de obtener una conexión del grupo". ¿Su solución de cadena de conexión resolvió esto, o fue un problema de apretón de manos por separado?
FBryant87
1
Por lo que recuerdo, fue exactamente el mismo mensaje de error que en la pregunta. Ocurrió inmediatamente después de actualizar a .NET Framework v4.6.1.
ajbeaven
Este también fue mi caso, lo arreglé en un servicio de aplicaciones que estaba ejecutando en Azure, conexión a una base de datos SQL de Azure. Estaba usando Dapper y desechando correctamente las conexiones, pero todavía recibí el mensaje de error "tiempo de espera transcurrido antes de obtener una conexión del grupo". Pero no más, así que gracias @ajbeaven
Steve Kennaird
13

A menos que su uso haya aumentado mucho, parece poco probable que solo haya una acumulación de trabajo. En mi opinión, la opción más probable es que algo está usando conexiones y no las está liberando rápidamente. ¿Estás seguro de que estás usando usingen todos los casos? ¿O (a través de cualquier mecanismo) liberando las conexiones?

Marc Gravell
fuente
13

¿Comprobó si hay DataReaders que no están cerrados y response.redirects antes de cerrar la conexión o un lector de datos? Las conexiones permanecen abiertas cuando no las cierra antes de una redirección.

Ivo
fuente
3
+1 - O funciones que devuelven DataReaders - La conexión nunca se cerrará fuera de la función que las creó ...
splattne
1
Si su función devuelve SqlDataReader, es mejor convertirlo a DataTable que aumentar el tamaño máximo de la agrupación
live-love
10

Nos encontramos con este problema de vez en cuando en nuestro sitio web también. El culpable en nuestro caso es que nuestras estadísticas / índices se están desactualizando. Esto hace que una consulta que se ejecuta rápidamente anteriormente se vuelva (eventualmente) lenta y se agote el tiempo de espera.

Intente actualizar las estadísticas y / o reconstruir los índices en las tablas afectadas por la consulta y vea si eso ayuda.

Alex Czarto
fuente
3
Creo que esto explicaría por qué una consulta podría agotar el tiempo de espera, pero no creo que esto explique por qué se experimenta un tiempo de espera al intentar obtener una conexión.
DrGriff
1
Podría ser que con un índice incorrecto las consultas tarden más y se usen más conexiones al mismo tiempo.
LosManos
1
Me funcionó después de actualizar todas las estadísticas con sp_updatestats en un db: EXEC sp_updatestats;
boateng
6

Puede especificar el tamaño mínimo y máximo de la agrupación especificando MinPoolSize=xyzy / o MaxPoolSize=xyzen la cadena de conexión. Sin embargo, esta causa del problema podría ser algo diferente.

Mehrdad Afshari
fuente
3
¿Cuál es el MaxPoolSize recomendado?
Amr Elgarhy
2
Probablemente, la mejor manera de hacerlo es no especificarlo a menos que tenga requisitos especiales y conozca un buen tamaño de grupo para su caso específico .
Mehrdad Afshari
1
Nota: revisé y descubrí que las conexiones activas a mi base de datos son alrededor de 22 en vivo, ¿es eso demasiado?
Amr Elgarhy
No lo creo. El tamaño predeterminado del grupo es de 100 conexiones, creo. Depende de la carga de cada conexión en la red y el servidor SQL. Si están ejecutando consultas pesadas, puede causar problemas. Además, pueden surgir problemas de red al iniciar una nueva conexión y puede causar esa excepción.
Mehrdad Afshari
5

También he encontrado este problema cuando uso una capa de datos de terceros en una de mis aplicaciones .NET. El problema era que la capa no cerraba las conexiones correctamente.

Tiramos la capa y creamos una nosotros mismos, que siempre cierra y elimina las conexiones. Desde entonces ya no recibimos el error.

Wim Haanstra
fuente
Estamos utilizando LLBL y el sitio web funciona desde hace 2 años y solo los últimos días comenzaron a actuar así.
Amr Elgarhy
5

También puede probar eso, para resolver el problema del tiempo de espera:

Si no agregó httpRuntime a su configuración web, agréguelo a la <system.web>etiqueta

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

y

Modifique su cadena de conexión de esta manera;

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

Por ultimo uso

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

Esto se debe principalmente a que la conexión no se ha cerrado en la aplicación. Use "MinPoolSize" y "MaxPoolSize" en la cadena de conexión.

subramanian.m
fuente
3

En mi caso, no estaba cerrando el 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
fuente
2

Si está trabajando en un código heredado complejo en el que no es posible usar (..) {..} de manera simple, tal como estaba, puede consultar el fragmento de código que publiqué en esta pregunta SO para conocer la forma de determinar pila de llamadas de la creación de la conexión cuando una conexión se pierde potencialmente (no se cierra después de un tiempo de espera establecido). Esto hace que sea bastante fácil detectar la causa de las fugas.

LOAS
fuente
2

No cree instancias de la conexión sql demasiadas veces. Abra una o dos conexiones y úselas para todas las siguientes operaciones sql.

Parece que incluso cuando se Disposeconectan las conexiones se produce la excepción.


fuente
2

Además de las soluciones publicadas ...

Al tratar con 1000 páginas de código heredado, cada una llamando a un GetRS común varias veces, aquí hay otra manera de solucionar el problema:

En una DLL común existente, agregamos la opción 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);   
    }

Luego, en cada página, siempre que cierre el lector de datos, la conexión también se cerrará automáticamente para evitar fugas de conexión.

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

Simplemente tuve el mismo problema y quería compartir lo que me ayudó a encontrar la fuente: agregue el nombre de la aplicación a su cadena de conexión y luego motive la conexión abierta al servidor SQL

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
fuente
1

Este problema que tuve en mi código. Pegaré un código de ejemplo que he pasado por debajo del error. El tiempo de espera transcurrió antes de obtener una conexión del grupo. Esto puede haber ocurrido porque todas las conexiones agrupadas estaban en uso y se alcanzó el tamaño máximo de la agrupación.

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

Desea cerrar la conexión cada vez. Antes de eso, no nos conecté de cerca debido a esto recibí un error. Después de agregar una declaración cercana, he recibido este error

Aravindhan R
fuente
0

Has filtrado conexiones en tu código. Puede intentar usar el uso para certificar que los está cerrando.

 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/

EduLopez
fuente
0

Este problema me he encontrado antes. Terminó siendo un problema con el firewall. Acabo de agregar una regla al firewall. Tuve que abrir el puerto 1433para que el servidor SQL pueda conectarse al servidor.

Aswad Feisal
fuente
0

Utilizar este:

finally
{
    connection.Close();
    connection.Dispose();
    SqlConnection.ClearPool();
}
Jeno M
fuente
12
Tal vez me estoy perdiendo el punto SqlConnection.ClearPool, pero ¿eso solo impide que su conexión actual se libere al grupo de conexiones? Pensé que la idea del grupo de conexiones era permitir conexiones más rápidas. ¿Seguramente liberar la conexión del grupo cada vez que termine significa que se necesitará crear una NUEVA conexión CADA VEZ que se necesite una, en lugar de extraer una de repuesto del grupo? Por favor explique cómo y por qué esta técnica es útil.
Dib
0

Sí, hay una manera de cambiar la configuración. Si está en un servidor dedicado y simplemente necesita más conexiones SQL, puede actualizar las entradas de "tamaño máximo de agrupación" en ambas cadenas de conexión siguiendo estas instrucciones:

  1. Inicie sesión en su servidor usando Escritorio remoto
  2. Abra Mi PC (Windows - E) y vaya a C: \ inetpub \ vhosts [dominio] \ httpdocs
  3. Haga doble clic en el archivo web.config. Esto solo puede aparecer como web si la estructura del archivo está configurada para ocultar extensiones. Esto abrirá Visual Basic o un editor similar.
  4. Encuentre sus cadenas de conexión, estas se verán similares a los ejemplos a continuación:

    "add name =" SiteSqlServer "connectionString =" servidor = (local); base de datos = dbname; uid = dbuser; pwd = dbpassword; pooling = true; conexión de por vida = 120; tamaño máximo de pool = 25; ""

5. Cambie el tamaño máximo del grupo = valor X al tamaño del grupo requerido.

  1. Guarde y cierre su archivo web.config.
Srusti Thakkar
fuente
0

En mi caso, tenía un bucle infinito (de una propiedad get que intentaba obtener valor de la base de datos) que seguía abriendo cientos de conexiones SQL.

Para reproducir el problema intente esto:

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