sql >> Base de Datos >  >> RDS >> Database

Encuentre fugas de conexión de base de datos en su aplicación

Autor invitado:Michael J Swart (@MJSwart)

Recientemente nos sorprendió una serie de excepciones que lanzó nuestra aplicación. Nuestra aplicación fallaba al intentar abrir una conexión Sql. Las excepciones se veían así:

Error System.InvalidOperationException:

Tiempo agotado. 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.

Grupos de conexiones

Recuerde que .Net usa grupos de conexiones para ayudar a evitar la sobrecarga de establecer una conexión en cada consulta. Los grupos de conexiones se mantienen para cada cadena de conexión y, de forma predeterminada, el número de conexiones en el grupo tiene un límite de cien. Cien conexiones suelen ser suficientes. Nunca antes habíamos tenido un problema con esta excepción y nuestros servidores no estaban más ocupados de lo habitual, por lo que dudamos en aumentar el valor de MaxPoolSize. Empezamos a sospechar fugas en la conexión de la base de datos.
 

Fugas de conexión a la base de datos

Al igual que las fugas de memoria, las fugas de conexión a la base de datos pueden ocurrir si no se deshace de las conexiones de la base de datos de manera oportuna. SqlConnections son IDisposable, por lo que es una buena práctica usar la declaración de uso:

using (SqlConnection conn = new SqlConnection(connectionString)) 
{
  conn.Open();
  // etc...
}

Tan pronto como haya terminado con SqlConnection, se desecha y la conexión real regresa inmediatamente al grupo de conexiones para que otra persona pueda usarla. De lo contrario, la conexión permanece en uso hasta que finaliza el proceso o la recolección de elementos no utilizados la limpia.

Encontrar las fugas de conexión

Por lo tanto, si su aplicación experimenta tiempos de espera de conexión debido a una fuga de conexión a la base de datos, es posible que los seguimientos de la pila no le ayuden. Al igual que una excepción de falta de memoria debido a una fuga de memoria, el seguimiento de la pila tiene información sobre la víctima, pero no sobre la causa raíz. Entonces, ¿dónde puede ir para encontrar la fuga?
 
Aunque las fugas de conexión a la base de datos son un problema del cliente, puede encontrar ayuda en el servidor de la base de datos. En el servidor de la base de datos, mire las conexiones por proceso por base de datos para obtener una estimación aproximada del tamaño de cada grupo:

select count(*) as sessions,
         s.host_name,
         s.host_process_id,
         s.program_name,
         db_name(s.database_id) as database_name
   from sys.dm_exec_sessions s
   where is_user_process = 1
   group by host_name, host_process_id, program_name, database_id
   order by count(*) desc;

El nombre del programa, el nombre del host, la identificación del proceso y el nombre de la base de datos suelen ser lo suficientemente buenos para identificar las conexiones que provienen del mismo grupo de conexiones.

Esto me lleva a hacer algunas preguntas más sobre grupos con muchas conexiones. Dado un grupo, ¿hay sesiones que han estado inactivas durante un tiempo y, de ser así, cuánto tiempo han estado inactivas y cuál fue la última instrucción SQL que ejecutaron?

declare @host_process_id int = 1508;
  declare @host_name sysname = N'SERV4102';
  declare @database_name sysname = N'My_Database';
 
  select datediff(minute, s.last_request_end_time, getdate()) as minutes_asleep,
         s.session_id,
         db_name(s.database_id) as database_name,
         s.host_name,
         s.host_process_id,
         t.text as last_sql,
         s.program_name
    from sys.dm_exec_connections c
    join sys.dm_exec_sessions s
         on c.session_id = s.session_id
   cross apply sys.dm_exec_sql_text(c.most_recent_sql_handle) t
   where s.is_user_process = 1
         and s.status = 'sleeping'
         and db_name(s.database_id) = @database_name
         and s.host_process_id = @host_process_id
         and s.host_name = @host_name
         and datediff(second, s.last_request_end_time, getdate()) > 60
   order by s.last_request_end_time;

El texto ahora se puede usar para buscar en el código base de su aplicación para encontrar dónde puede tener una fuga de conexión a la base de datos.

Estas consultas son útiles para solucionar problemas de fugas en la conexión de la base de datos y también se pueden usar para crear un monitor o una verificación de estado.

¡Deseche sus desechables, use esos usos, selle esas fugas!

Sobre el autor

Michael J Swart es un bloguero y profesional de bases de datos apasionado que se centra en el desarrollo de bases de datos y la arquitectura de software. Le gusta hablar sobre cualquier tema relacionado con los datos, contribuyendo a proyectos comunitarios. Michael escribe en un blog como "Database Whisperer" en michaeljswart.com.