2010-10-08 25 views
24

Como el título sugiere que tengo un problema con la primera consulta en una base de datos de SQL Server con Entity Framework.Entidad Framework - Primera consulta lenta

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 que incluyen dos relaciones 0-many.

Las pruebas se realizaron en Visual Studio 2010 utilizando el modelo Entity Framework 4.0 y el generador POCO (no hay mucha diferencia en los tiempos entre entidades normales y objetos POCO). También utilicé la Plantilla de Vistas T4 para precompilar las vistas.

La base de datos fue en SQL Server 2008.

Lo que me gustaría saber es por qué la primera consulta es taaan mucho más lenta que cualquier consulta secundarias.

También quiero saber si se puede hacer algo para aumentar la velocidad de la primera consulta hasta un punto en el que esté dentro de los límites aceptables.

Esta es una gran consulta y podemos obtener otras consultas que son aún mayores 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 tratar de averiguar dónde está el problema y me sorprendió un poco ver que parece que es el SQL Server el que tarda en la primera consulta.

sincronizaciones fue la siguiente:

.NET aplicación de prueba:

  • primera consulta: 29,6 segundos
  • segunda consulta: 3,2 segundos

SQL Profiler:

  • First Qu ery: 27 segundos
  • segunda consulta: 3,2 segundos

SQL Server ventana Query

  • primera consulta: 8 segundos
  • segunda consulta: 4 segundos

Timings en la aplicación se midió con la clase Stopwatch. Solo se midió la consulta y se utilizó .ToList() para ejecutar la consulta.

Las temporizaciones en SQL Server Profiler son para las mismas consultas que se ejecutaron en la aplicación que muestran que la aplicación solo utiliza aproximadamente 2,6 segundos para completar 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 la ejecución de la consulta es mucho más rápida esta vez.

Entiendo por qué la aplicación no utiliza ningún momento porque no hay nuevas filas que deban convertirse en objetos, pero ¿por qué la consulta es mucho más rápida ?, habría esperado unos segundos debido a los planes de ejecución, pero no 24 segundos.

Solo para fines de prueba copié el SQL que genera Entity Framework y abrí una nueva ventana de consulta con una conexión separada y ejecuté la consulta en ella.

Como puede ver, tarda 8 segundos para la primera consulta y 4 segundos para la segunda.

Espero que alguien tenga algunas sugerencias.

ps. Me disculpo por el muro de texto :)

Editar 19-10-2010:
Hice una prueba ayer que parece ser compatible con que las filas se devuelvan de forma secuencial. Lo que significa que cuando se devuelve una fila de la base de datos se materializa inmediatamente (si no existe en el contexto), se devuelve la siguiente fila y así sucesivamente.

Es por eso que parece que la consulta lleva mucho tiempo en el servidor de la base de datos porque el tiempo de materialización se incluye en los tiempos del perfilador de SQL Server.

No creo que 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.

  1. Ejecutar la primera consulta con EF, la instrucción SQL es más lento que cualquier consulta secundaria
  2. Desechar el contexto/repositorio
  3. Crear un nuevo contexto
  4. ejecutar la misma consulta que antes (de nuevo la primera la consulta es lenta y también la instrucción SQL)

Es casi como que 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, ya que recuerdo que la consulta se compila la primera vez que se utiliza, lo que significa que la primera consulta tardará 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 donde creé una consulta compilada como estática para que se compilara para todos los contextos que se crearon.

Luego creé un contexto, ejecuté la consulta, destruí el contexto y creé uno nuevo y ejecuté la misma consulta una vez más.

La diferencia no fue tan grande, solo unos segundos y la primera vez que ejecuté la consulta, me tomó todo el tiempo posible sin precompilarla.

En cuanto a la generación de vistas, ya lo implementamos utilizando plantillas T4.

¿La respuesta es realmente que EF solo funciona si no se hace nada más que las consultas más simples que devuelven solo una cantidad relativamente pequeña de datos?

Respuesta

4

Bueno, Los lotes pueden hacer que una consulta de SQL Server sea más lenta la primera vez que se ejecuta. La mayoría de ellos no toman varios segundos, sin embargo.

... 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 del almacenamiento en el 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 utilizar el tipo CompiledQuery para precompilar las consultas de Entity Framework para hacer este trabajo antes de tiempo, antes de que el usuario final tenga que esperarlo.

En un modelo muy grande, la generación de vistas toma algo de tiempo, también. Puede mover esto para compilar el tiempo, en su lugar. Consulte this article para obtener más consejos.

+0

Disculpa la respuesta tardía. No creo que 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. P.ej. 1) Ejecute la primera consulta con EF, la instrucción SQL es más lenta que cualquier consulta secundaria. 2) Eliminar el contexto/repositorio. 3) Crea un nuevo contexto. 4) Ejecuta la misma consulta que antes. (de nuevo, la primera consulta es lenta y también lo es la declaración de SQL) Es casi como que EF envía algunas opciones junto con la primera consulta que hace que el servidor sea lento. –

+0

En cuanto a la compilación de consultas, creo que la consulta se compila la primera vez que se utiliza, es decir, la primera consulta tardará 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 realicé una prueba donde creé una consulta compilada estática para que se compilara para todos los contextos que se crearon. Luego creé un contexto, ejecuté la consulta, eliminé el contexto, creé uno nuevo y ejecuté la consulta nuevamente. La diferencia no fue tan grande, solo unos pocos segundos, y la primera vez que ejecuté la consulta, me tomó todo el tiempo sin precompilarla. –

+0

En cuanto a View Generation, ya implementamos esto usando plantillas T4. ¿La respuesta es realmente que EF solo funciona si no haces nada más que las consultas más simples que devuelven solo una cantidad relativamente pequeña de datos? –

13

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 tiene que ocurrir una vez por Dominio de aplicación. No he encontrado ninguna compilación previa 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 inicia la aplicación. Esto carga los metadatos, aún tarda mucho tiempo (18-19 segundos en nuestro caso), pero la aplicación responde durante la carga. Además, la primera carga real no toma tanto tiempo.

Tenga en cuenta que, en nuestro contexto, es posible que el usuario dedique entre 18 y 19 segundos a la aplicación antes de que deba realizar una llamada EF en respuesta a sus acciones. Obviamente, si esto no es posible en su aplicación, este trabajo puede no proporcionar un aumento de velocidad.

0

Tenemos el mismo problema. Es solo con el primer enfoque del Código. Tenemos aproximadamente 1500 POCO (+1500 archivos de mapeo POCO). Solo compilar toma de 1 a 2 minutos. El método Context.table.Add() tarda unos 3-4 minutos, pero para el primer objeto. Es como una mala broma sin solución en ninguna parte. Esos 3-4 minutos es probablemente algún tipo de EF "transformación POCO". Un núcleo de CPU se ejecuta al 100% y no ocurre nada en el generador de perfiles SQL.

El primer enfoque de la base de datos (generar el archivo edmx xml) para las mismas 1500 tablas funciona normalmente. Es rápido como se esperaba

No hay solución en ningún lado hasta el momento. Quizás EF6 resolverá esto.

+2

Este es exactamente el mismo escenario que he encontrado. Pero, esta no es realmente una respuesta. Sería más apropiado comunicar esto a través de un comentario. – quakkels

+0

En EF6 tengo la misma situación: el primer enfoque de la base de datos es rápido, Poco es lento –

6

Tuve el mismo problema. Y usé un truco para resolver ese problema. Como el marco Entity tarda un poco más de tiempo en ser el primero acceso y luego almacena en caché parte del resultado la primera vez en su nivel (el servidor sql también almacena en caché el resultado por separado). Así que accedí al marco Entity en Mi aplicación para 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); 
    } 
+0

Después de muchas horas de investigación, esto de hecho aceleró mis consultas "frías". –

+0

¡Impresionante, me funciona! Gracias – AllmanTool

1

yo sólo me encontré con este post en mi búsqueda para mejorar el tiempo de inicio de EF.Dado que realmente no tiene una respuesta, agregaré mis hallazgos para que otras personas puedan aprovecharlo si tropiezan con esta publicación también.

Tenga en cuenta que estoy usando EF 6, y la solución sólo es aplicable para EF 6.

David Roth ha publicado una article que aborda el problema.

Mikael Eliasson lo resumió muy bien en su answer a una pregunta similar:

  1. El uso de un modelo de tienda db caché
  2. Generar vistas pre-compilados
  3. Generar versión pre-compilada de ADO.NET Entity Framework utilizando n -gen para evitar jitting
Cuestiones relacionadas