Share via


Diagnóstico del rendimiento

En esta sección se describen las formas de detectar problemas de rendimiento en la aplicación de EF y, una vez que se ha identificado un área problemática, cómo analizarla aún más para identificar el problema raíz. Es importante diagnosticar e investigar cuidadosamente los problemas antes de saltar a las conclusiones y evitar asumir dónde está la raíz del problema.

Identificación de comandos de base de datos lentos mediante el registro

Al final del día, EF prepara y ejecuta comandos que se ejecutarán en la base de datos; con la base de datos relacional, esto significa ejecutar instrucciones SQL a través de la API de base de datos de ADO.NET. Si una consulta determinada tarda demasiado tiempo (por ejemplo, porque falta un índice), esto se puede detectar inspeccionando los registros de ejecución de comandos y observando cuánto tiempo tardan realmente.

EF facilita la captura de los tiempos de ejecución de comandos mediante el registro sencillo o Microsoft.Extensions.Logging:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder
        .UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True;ConnectRetryCount=0")
        .LogTo(Console.WriteLine, LogLevel.Information);
}

Cuando el nivel de registro se establece en LogLevel.Information, EF emite un mensaje de registro para cada ejecución de comandos con el tiempo necesario:

info: 06/12/2020 09:12:36.117 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command)
      Executed DbCommand (4ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      SELECT [b].[Id], [b].[Name]
      FROM [Blogs] AS [b]
      WHERE [b].[Name] = N'foo'

El comando anterior tardó 4 milisegundos. Si un determinado comando tarda más de lo esperado, ha encontrado un posible culpable de un problema de rendimiento y ahora puede centrarse en él para comprender por qué se ejecuta lentamente. El registro de comandos también puede revelar casos en los que se realizan recorridos de ida y vuelta inesperados de la base de datos; esto se mostraría como varios comandos en los que solo se esperaba uno.

Advertencia

Dejar el registro de ejecución de comandos habilitado en el entorno de producción suele ser una mala idea. El propio registro ralentiza la aplicación y puede crear rápidamente archivos de registro enormes que pueden llenar el disco del servidor. Se recomienda seguir iniciando sesión durante un breve intervalo de tiempo para recopilar datos (mientras supervisa cuidadosamente la aplicación) o para capturar datos de registro en un sistema de preproducción.

Correlación de comandos de base de datos a consultas LINQ

Un problema con el registro de ejecución de comandos es que a veces es difícil correlacionar consultas SQL y consultas LINQ: los comandos SQL ejecutados por EF pueden ser muy diferentes de las consultas LINQ desde las que se generaron. Para ayudar con esta dificultad, es posible que desee usar la característica de etiquetas de consulta de EF, lo que le permite insertar un comentario pequeño e identificarlo en la consulta SQL:

var myLocation = new Point(1, 2);
var nearestPeople = (from f in context.People.TagWith("This is my spatial query!")
                     orderby f.Location.Distance(myLocation) descending
                     select f).Take(5).ToList();

La etiqueta se muestra en los registros:

-- This is my spatial query!

SELECT TOP(@__p_1) [p].[Id], [p].[Location]
FROM [People] AS [p]
ORDER BY [p].[Location].STDistance(@__myLocation_0) DESC

A menudo vale la pena etiquetar las consultas principales de una aplicación de esta manera, para que los registros de ejecución de comandos sean más legibles de inmediato.

Otras interfaces para capturar datos de rendimiento

Hay varias alternativas a la característica de registro de EF para capturar tiempos de ejecución de comandos, lo que puede ser más eficaz. Las bases de datos suelen incluir sus propias herramientas de seguimiento y análisis de rendimiento, que normalmente proporcionan información mucho más completa y específica de la base de datos más allá de tiempos de ejecución simples; la configuración real, las funcionalidades y el uso varían considerablemente entre las bases de datos.

Por ejemplo, SQL Server Management Studio es un cliente eficaz que puede conectarse a la instancia de SQL Server y proporcionar información valiosa de administración y rendimiento. No entra en el alcance de esta sección entrar en detalles, pero dos capacidades que merece la pena mencionar son el Monitor de actividad, que proporciona un panel en directo de la actividad del servidor (incluidas las consultas más costosas), y la característica Eventos ampliados (XEvent), que permite definir sesiones arbitrarias de captura de datos que pueden adaptarse a sus necesidades exactas. La documentación de SQL Server sobre la supervisión proporciona más información sobre estas características, entre otras.

Otro enfoque para capturar datos de rendimiento es recopilar información emitida automáticamente por EF o el controlador de base de datos a través de la interfaz de DiagnosticSource y, a continuación, analizar esos datos o mostrarlos en un panel. Si usa Azure, Azure Application Insights proporciona una supervisión eficaz de forma inmediata, integrando el rendimiento de la base de datos y los tiempos de ejecución de consultas en el análisis de la rapidez con la que se sirven las solicitudes web. Puede encontrar más información sobre esto en el tutorial de rendimiento de Application Insights y en la página de Análisis de Azure SQL.

Inspección de planes de ejecución de consultas

Una vez que haya identificado una consulta problemática que requiere optimización, el siguiente paso suele analizar el plan de ejecución de la consulta. Cuando las bases de datos reciben una instrucción SQL, normalmente producen un plan de cómo se va a ejecutar ese plan; esto a veces requiere una toma de decisiones complicada basada en qué índices se han definido, cuántos datos existen en las tablas, etc. (casualmente, el propio plan normalmente debería estar almacenado en caché en el servidor para un rendimiento óptimo). Normalmente, las bases de datos relacionales proporcionan una manera de que los usuarios vean el plan de consulta, junto con el costo calculado de diferentes partes de la consulta, lo es muy valioso para mejorar las consultas.

Para empezar a trabajar en SQL Server, consulte la documentación sobre los planes de ejecución de consultas. El flujo de trabajo de análisis típico sería usar SQL Server Management Studio, pegar el SQL de una consulta lenta identificada a través de uno de los medios anteriores y generar un plan de ejecución gráfico:

Display a SQL Server execution plan

Aunque los planes de ejecución pueden parecer complicados al principio, vale la pena dedicar un poco de tiempo a familiarizarse con ellos. Es especialmente importante tener en cuenta los costos asociados a cada nodo del plan e identificar cómo se usan los índices (o no) en los distintos nodos.

Aunque la información anterior es específica de SQL Server, otras bases de datos suelen proporcionar el mismo tipo de herramientas con una visualización similar.

Importante

Las bases de datos a veces generan diferentes planes de consulta en función de los datos reales de la base de datos. Por ejemplo, si una tabla contiene solo unas pocas filas, una base de datos puede optar por no usar un índice en esa tabla, sino realizar un recorrido de tabla completo en su lugar. Si analiza planes de consulta en una base de datos de prueba, asegúrese siempre de que contiene datos similares al sistema de producción.

Contadores de eventos

Las secciones anteriores se centraron en cómo obtener información sobre los comandos y cómo se ejecutan estos comandos en la base de datos. Además de eso, EF expone un conjunto de contadores de eventos que proporcionan más información de nivel inferior sobre lo que sucede dentro de EF en sí y cómo la aplicación lo usa. Estos contadores pueden ser muy útiles para diagnosticar problemas de rendimiento específicos y anomalías de rendimiento, como problemas de almacenamiento en caché de consultas que provocan una recompilación constante, pérdidas de DbContext no disponibles y otros.

Consulte la página dedicada de los contadores de eventos de EF para más información.

Pruebas comparativas con EF Core

Al final del día, a veces es necesario saber si una forma determinada de escribir o ejecutar una consulta es más rápida que otra. Es importante no suponer ni especular nunca la respuesta, y es extremadamente fácil elaborar un punto de referencia rápido para obtenerla. Al escribir puntos de referencia, se recomienda encarecidamente usar la conocida biblioteca BenchmarkDotNet, que controla muchos de los problemas que los usuarios encuentran al intentar escribir sus propios puntos de referencia: ¿ha realizado algunas iteraciones de preparación? ¿Cuántas iteraciones ejecuta realmente el punto de referencia y por qué? Veamos qué aspecto tiene un punto de referencia con EF Core.

Sugerencia

El proyecto de punto de referencia completo para el origen que figura a continuación está disponible aquí. Se recomienda copiarlo y usarlo como plantilla para sus propios puntos de referencia.

Como escenario de punto de referencia sencillo, comparemos los siguientes métodos diferentes para calcular la clasificación media de todos los blogs de nuestra base de datos:

  • Cargue todas las entidades, sume sus clasificaciones individuales y calcule el promedio.
  • Al igual que en el caso anterior, solo use una consulta sin seguimiento. Esto debería ser más rápido, ya que no se realiza la resolución de identidades y no se toman instantáneas de las entidades a efectos del seguimiento de los cambios.
  • Evite cargar todas las instancias de la entidad Blog, proyectando solo la clasificación. Así nos ahorramos transferir las otras columnas innecesarias del tipo de entidad Blog.
  • Calcule el promedio de la base de datos haciendo que forme parte de la consulta. Esta debería ser la forma más rápida, ya que todo se calcula en la base de datos y solo se transfiere el resultado al cliente.

Con BenchmarkDotNet, se escribe el código que se va a comparar como un método simple (al igual que una prueba unitaria) y BenchmarkDotNet ejecuta automáticamente cada método para un número suficiente de iteraciones, midiendo de forma confiable cuánto tiempo se tarda y cuánta memoria se asigna. Estos son los diferentes métodos (el código de punto de referencia completo puede verse aquí):

[Benchmark]
public double LoadEntities()
{
    var sum = 0;
    var count = 0;
    using var ctx = new BloggingContext();
    foreach (var blog in ctx.Blogs)
    {
        sum += blog.Rating;
        count++;
    }

    return (double)sum / count;
}

A continuación se muestran los resultados, tal como se imprime en BenchmarkDotNet:

Método Media Error StdDev Valor medio Proporción RatioSD Gen 0 Gen 1 Gen 2 Asignado
LoadEntities 2,860.4 us 54.31 us 93.68 us 2,844.5 us 4.55 0.33 210.9375 70.3125 - 1309.56 KB
LoadEntitiesNoTracking 1,353.0 us 21.26 us 18.85 us 1,355.6 us 2,10 0.14 87.8906 3.9063 - 540.09 KB
ProjectOnlyRanking 910.9 us 20.91 us 61.65 us 892.9 us 1.46 0.14 41.0156 0.9766 - 252.08 KB
CalculateInDatabase 627.1 us 14.58 us 42.54 us 626.4 us 1.00 0,00 4.8828 - - 33.27 KB

Nota:

A medida que los métodos instancian y disponen el contexto dentro del método, estas operaciones se contabilizan para el punto de referencia, aunque estrictamente hablando no forman parte del proceso de consulta. Esto no debería importar si el objetivo es comparar dos alternativas entre sí (ya que la instanciación del contexto y la eliminación son las mismas), y proporciona una medición más holística de toda la operación.

Una limitación de BenchmarkDotNet es que mide el rendimiento simple, de un solo subproceso, de los métodos que proporciona y, por lo tanto, no es muy adecuado para la evaluación comparativa de escenarios de simultaneidad.

Importante

Asegúrese siempre de tener datos en su base de datos que sean similares a los de producción cuando realice la evaluación comparativa, de lo contrario los resultados podrían no representar el rendimiento real en producción.