Introducción al rendimiento

El rendimiento de la base de datos es un tema amplio y complejo, que abarca una pila completa de componentes: la base de datos, las redes, el controlador de base de datos y las capas de acceso a datos, como EF Core. Aunque las capas de alto nivel y los O/RMs, como EF Core, simplifican considerablemente el desarrollo de aplicaciones y mejoran su mantenibilidad, a veces pueden ser opacos, ocultando detalles internos críticos para el rendimiento, como el SQL que se ejecuta. En esta sección se intenta proporcionar información general sobre cómo lograr un buen rendimiento con EF Core y cómo evitar problemas comunes que pueden degradar el rendimiento de las aplicaciones.

Identificar cuellos de botella y medir, medir, medir

Como siempre con el rendimiento, es importante no acelerar la optimización sin datos que muestren un problema; como dijo el gran Donald Knuth una vez, "La optimización prematura es la raíz de todo mal". En la sección de diagnóstico de rendimiento se describen varias maneras de comprender dónde la aplicación pasa tiempo en la lógica de la base de datos y cómo identificar áreas problemáticas específicas. Una vez identificada una consulta lenta, se pueden considerar soluciones: ¿falta un índice en la base de datos? ¿Debe probar otros patrones de consulta?

Comparar siempre el código y posibles alternativas: la sección de diagnóstico de rendimiento contiene una prueba comparativa de ejemplo con BenchmarkDotNet, que puede usar como plantilla para sus propias pruebas comparativas. No asuma que las pruebas comparativas públicas generales se aplican as-is a su caso de uso específico; diversos factores, como la latencia de la base de datos, la complejidad de las consultas y las cantidades de datos reales de las tablas, pueden tener un efecto profundo en qué solución es mejor. Por ejemplo, muchas pruebas comparativas públicas se llevan a cabo en condiciones de red ideales, donde la latencia de la base de datos es casi cero y con consultas extremadamente ligeras que apenas requieren ningún procesamiento (o E/S de disco) en la base de datos. Aunque son útiles para comparar las sobrecargas en tiempo de ejecución de diferentes capas de acceso a datos, las diferencias que muestran suelen ser insignificantes en una aplicación real, donde la base de datos realiza el trabajo real y la latencia de la base de datos es un factor de rendimiento significativo.

Aspectos del rendimiento del acceso a datos

El rendimiento general del acceso a datos se puede dividir en las siguientes categorías generales:

  • Rendimiento de la base de datos pura. Con la base de datos relacional, EF traduce las consultas LINQ de la aplicación en las instrucciones SQL que ejecuta la base de datos; estas instrucciones SQL pueden ejecutarse de forma más o menos eficaz. El índice correcto en el lugar correcto puede marcar un mundo de diferencia en el rendimiento de SQL o volver a escribir la consulta LINQ puede hacer que EF genere una mejor consulta SQL.
  • Transferencia de datos de red. Al igual que con cualquier sistema de red, es importante limitar la cantidad de datos que van de ida y vuelta en la conexión. Esto abarca asegurarse de que solo se envían y cargan los datos que realmente va a necesitar, pero también evitar el denominado efecto "explosión cartesiana" al cargar entidades relacionadas.
  • Viajes de ida y vuelta de red Más allá de la cantidad de datos que van de ida y vuelta, los viajes de ida y vuelta de la red, ya que el tiempo necesario para que una consulta se ejecute en la base de datos puede ser insignificante comparado con el tiempo que los paquetes se desplazan de ida y vuelta entre la aplicación y la base de datos. La sobrecarga de ida y vuelta depende en gran medida de su entorno; cuanto más lejos esté el servidor de bases de datos, mayor será la latencia y más costosa será cada ida y vuelta. Con la llegada de la nube, las aplicaciones se encuentran cada vez más lejos de la base de datos y las aplicaciones "chatty" que realizan demasiados recorridos de ida y vuelta experimentan un rendimiento degradado. Por lo tanto, es importante comprender exactamente cuándo la aplicación se pone en contacto con la base de datos, cuántos recorridos de ida y vuelta realiza y si ese número se puede minimizar.
  • Sobrecarga en tiempo de ejecución de EF. Por último, EF agrega cierta sobrecarga en tiempo de ejecución a las operaciones de base de datos: EF debe compilar las consultas de LINQ a SQL (aunque normalmente debería realizarse solo una vez), el seguimiento de cambios agrega cierta sobrecarga (pero se puede deshabilitar), etc. En la práctica, es probable que la sobrecarga de EF para las aplicaciones reales sea insignificante en la mayoría de los casos, ya que el tiempo de ejecución de consultas en la base de datos y la latencia de red dominan el tiempo total; pero es importante comprender cuáles son las opciones y cómo evitar algunos problemas.

Saber lo que está sucediendo bajo la capucha

EF permite a los desarrolladores concentrarse en la lógica de negocios mediante la generación de SQL, la materialización de resultados y la realización de otras tareas. Al igual que cualquier capa o abstracción, también tiende a ocultar lo que sucede en segundo plano, como las consultas SQL reales que se ejecutan. El rendimiento no es necesariamente un aspecto crítico de todas las aplicaciones, pero en las aplicaciones donde está, es fundamental que el desarrollador comprenda lo que EF está haciendo para ellos: inspeccionar las consultas SQL salientes, seguir recorridos de ida y vuelta para asegurarse de que el problema de N+1 no se está produciendo, etc.

Caché fuera de la base de datos

Por último, la manera más eficaz de interactuar con una base de datos es no interactuar con ella en absoluto. En otras palabras, si el acceso a la base de datos se muestra como un cuello de botella de rendimiento en la aplicación, puede ser útil almacenar en caché determinados resultados fuera de la base de datos, para minimizar las solicitudes. Aunque el almacenamiento en caché agrega complejidad, es una parte especialmente crucial de cualquier aplicación escalable: mientras que el nivel de aplicación se puede escalar fácilmente agregando servidores adicionales para controlar una mayor carga, el escalado del nivel de base de datos suele ser mucho más complicado.