.net - slow - Entity Framework-Primera consulta lenta
entity framework slow (5)
Como sugiere el título, tengo un problema con la primera consulta en una base de datos de SQL Server utilizando Entity Framework.
He intentado buscar una respuesta en diferentes sitios, pero nadie parece tener una solución para esto.
Estoy cargando bastantes filas de la base de datos, incluidas dos relaciones de 0 a muchos.
Las pruebas se realizaron en Visual Studio 2010 utilizando el modelo de Entity Framework 4.0 y el generador de POCO (no hay mucha diferencia en los tiempos entre las entidades normales y los objetos de POCO). También utilicé la plantilla de vistas T4 para precompilar las vistas.
La base de datos estaba en SQL Server 2008.
Lo que realmente me gustaría saber es por qué la primera consulta es mucho más lenta que cualquier otra consulta secundaria.
También quiero saber si se puede hacer algo para aumentar la velocidad de la primera consulta hasta un punto donde se encuentre dentro de los límites aceptables.
Esta es una consulta grande y podemos obtener otras consultas aún más grandes y es comprensible que puedan ser un poco lentas, pero 30 segundos es demasiado lento para que el usuario espere, especialmente cuando los conjuntos de datos pueden obtener los mismos datos mucho más rápido.
He hecho algunas pruebas de tiempo para intentar averiguar dónde está el problema y me sorprendió un poco ver que parece que es el servidor SQL que es lento en la primera consulta.
Los tiempos fueron los siguientes:
Aplicación de prueba .NET:
- Primera consulta: 29,6 segundos.
- Segunda consulta: 3,2 segundos
Analizador de SQL:
- Primera consulta: 27 segundos.
- Segunda consulta: 3,2 segundos
Ventana de consulta de SQL Server
- Primera consulta: 8 segundos
- Segunda consulta: 4 segundos
Los tiempos en la aplicación se midieron con la clase Stopwatch
. Solo se midió la consulta y se .ToList()
para ejecutar la consulta.
Los tiempos en el Analizador de SQL Server son para las mismas consultas que se ejecutaron en la aplicación, lo que muestra que la aplicación solo usa alrededor de 2,6 segundos para completar los datos en los objetos.
Los últimos 27 segundos se utilizan para ejecutar la consulta en SQL Server.
En cuanto a la consulta secundaria, los tiempos son los mismos tanto para la aplicación como para el servidor SQL, pero esta vez la ejecución de la consulta es mucho más rápida.
Puedo entender por qué la aplicación no se usa en ningún momento porque no hay filas nuevas que deban convertirse en objetos, pero ¿por qué la consulta es mucho más rápida? Hubiera esperado algunos segundos debido a los planes de ejecución pero no a los 24 segundos.
Solo con fines de prueba, copié el SQL que Entity Framework genera, abrí una nueva ventana de consulta con una conexión independiente y ejecuté la consulta en ella.
Como puede ver, toma 8 segundos para la primera consulta y 4 segundos para la segunda.
Espero que alguien tenga alguna sugerencia.
PD. Me disculpo por la pared de texto :)
Editar 19-10-2010:
Ayer hice una prueba que parece admitir que las filas se devuelven de manera secuencial. Lo que significa que cuando se devuelve una fila de la base de datos, se materializa inmediatamente (si no existe en el contexto), luego se devuelve la siguiente fila y así sucesivamente.
Es por eso que parece que la consulta lleva mucho tiempo en el servidor de base de datos porque el tiempo de materialización se incluye en los tiempos del perfilador de SQL Server.
No creo que este sea un caso de lectura de SQL Server desde el disco duro. La consulta lenta ocurre cada vez que hay una "primera consulta" en EF.
ex.
- Ejecute la primera consulta con EF, la instrucción SQL es más lenta que cualquier consulta secundaria
- Disponer el contexto / repositorio
- Crear un nuevo contexto
- Ejecute la misma consulta que antes (nuevamente, la primera consulta es lenta y también lo es la instrucción SQL)
Es casi como EF envía algunas opciones junto con la primera consulta que hace que el servidor sea lento.
En cuanto a la compilación de consultas, recuerdo que la consulta se compiló la primera vez que se usó, lo que significa que la primera consulta tardaría aún más en ejecutarse.
Las consultas secundarias serían más rápidas, pero la velocidad en las consultas secundarias no es el problema.
También hice una prueba en la que creé una consulta compilada como estática para que se compilara para todos los contextos creados.
Luego creé un contexto, ejecuté la consulta, destruí el contexto, creé uno nuevo y ejecuté la misma consulta una vez más.
La diferencia no fue tan grande, solo unos pocos segundos y la primera vez que ejecuté la consulta, me tomó tanto tiempo como sin compilarla previamente.
En cuanto a la generación de vistas, ya implementamos esto utilizando plantillas T4.
¿Realmente es la respuesta que EF solo funciona si no hace nada más que las consultas más simples que devuelven solo una cantidad relativamente pequeña de datos?
Bueno, muchas cosas pueden hacer que una consulta de SQL Server sea más lenta la primera vez que se ejecuta. Sin embargo, la mayoría de ellos no toman varios segundos.
… Excepto los accesos aleatorios al disco duro. La primera vez que ejecuta la consulta, es posible que SQL Server tenga que leer las páginas de la base de datos desde el almacenamiento del disco duro. La próxima vez que ejecute la consulta, es probable que esas páginas estén en la memoria.
Con respecto a Entity Framework, la primera vez que ejecuta una consulta debe compilarse en SQL. Puede usar el tipo CompiledQuery
para compilar previamente las consultas de Entity Framework con el fin de hacer este trabajo antes de tiempo, antes de que el usuario final tenga que esperar.
En un modelo muy grande, la generación de vistas también toma algo de tiempo. Puede mover esto para compilar el tiempo, en su lugar. Ver este artículo para más consejos de este tipo.
Me topé con esta publicación en mi búsqueda para mejorar el tiempo de inicio de EF. Ya que realmente no tiene una respuesta, agregaré mis conclusiones para que otras personas puedan aprovecharla si se topan con esta publicación también.
Tenga en cuenta que estoy usando EF 6, y la solución solo es aplicable para EF 6.
David Roth publicó un article que aborda el problema.
Mikael Eliasson lo resumió muy bien en su answer a una pregunta similar:
- Usando un almacén de modelo de db en caché
- Generar vistas precompiladas
- Genera una versión precompilada de entityframework usando n-gen para evitar jits
Tenemos el mismo problema. Es solo con el primer acercamiento al Código. Tenemos alrededor de 1500 POCO (+1500 archivos de mapeo de POCO). Sólo compilar toma alrededor de 1-2 minutos. El método Context.table.Add () toma alrededor de 3-4 minutos, pero para el primer objeto. Es como un chiste malo sin solución en ninguna parte. Esos 3-4 minutos es probablemente una especie de "transformación POCO" de EF. Un núcleo de CPU se ejecuta al 100% y no hay nada en el analizador de SQL.
El primer enfoque de base de datos (generar el archivo edmx xml) para las mismas 1500 tablas funciona normalmente. Es tan rápido como se esperaba.
No hay solución en ningún lugar hasta ahora. Tal vez EF6 resuelva esto.
Tuve el mismo problema. Y utilicé un truco para resolver ese problema. Como Entity framework tarda un poco más de tiempo al acceder por primera vez y luego almacena en caché parte del resultado por primera vez en su nivel (el servidor SQL también almacena en caché el resultado por separado). Así que accedí al Entity framework en Mi aplicación iniciar de forma asíncrona. Funcionó para mí. Y mi aplicación se volvió más suave.
En la página global.asax
protected void Application_Start()
{
Start(() =>
{
using (EF.DMEntities context = new EF.DMEntities())
{
context.DMUsers.FirstOrDefault();
}
});
}
private void Start(Action a)
{
a.BeginInvoke(null, null);
}
Tuvimos el mismo problema en EF 5.0 y, a partir de hoy, una búsqueda superficial en Google no revela una aceleración suficiente.
De acuerdo con este enlace http://msdn.microsoft.com/en-us/library/cc853327(v=vs.100).aspx "Cargando metadatos" conlleva un costo de tiempo moderado pero solo debe ocurrir una vez por AppDomain. No he encontrado ninguna precompilación como trucos para cargar los metadatos.
La solución que hemos implementado es hacer una consulta menor en el Contexto en un hilo separado cuando se inicie la aplicación. Esto carga los metadatos, todavía lleva mucho tiempo (18-19 segundos en nuestro caso), pero la aplicación responde durante la carga. También la primera carga real no toma tanto tiempo.
Tenga en cuenta que, en nuestro contexto, es posible que el usuario permanezca entre 18 y 19 segundos en la aplicación antes de que se deba realizar una llamada EF en respuesta a sus acciones. Obviamente, si esto no es posible en su aplicación, es posible que este trabajo no proporcione mucho aumento de velocidad.