Entity Framework es demasiado lento. ¿Cuáles son mis opciones? [cerrado]

93

Seguí el mantra "No optimizar prematuramente" y codifiqué mi servicio WCF usando Entity Framework.

Sin embargo, hice un perfil del rendimiento y Entity Framework es demasiado lento. (Mi aplicación procesa 2 mensajes en aproximadamente 1.2 segundos, mientras que la aplicación (heredada) que estoy reescribiendo hace 5-6 mensajes al mismo tiempo (la aplicación heredada llama a sprocs para su DB Access).

Mi perfil apunta a que Entity Framework toma la mayor parte del tiempo por mensaje.

¿Entonces cuales son mis opciones?

  • ¿Hay mejores ORM por ahí?
    (Algo que solo admita la lectura y escritura normal de objetos y lo haga rápido ..)

  • ¿Hay alguna forma de hacer que Entity Framework sea más rápido?
    ( Nota : cuando digo más rápido me refiero a largo plazo, no a la primera llamada. (La primera llamada es lenta (15 segundos para un mensaje), pero eso no es un problema. Solo necesito que sea rápido para el resto de los mensajes.)

  • Una tercera opción misteriosa que me ayudará a obtener más velocidad de mi servicio.

NOTA: La mayoría de mis interacciones de base de datos son Crear y Actualizar. Hago muy poco seleccionando y eliminando.

Vaccano
fuente
Esto suena como una repetición de 'linq is slow', ¿cómo sabes que es EF? ¿Ha perfilado todos sus cambios?
Maess
6
Algunas de las respuestas apuntan a las consultas. En mi experiencia, la lentitud en EF tiene poco que ver con las consultas, sino con los costos de materialización, y esos costos a menudo están vinculados al seguimiento de cambios y cómo eso afecta a las instancias creadas. Desafortunadamente, no tengo una solución milagrosa para usted, así que esto es solo un comentario, pero recomendaría ver si la elaboración de perfiles revela altos costos de materialización y, de ser así, investigue qué se puede hacer con dichos costos.
Anthony Pegram
@Maess: pensé haber indicado que había perfilado y descubrí que era EF / DB el que era lento. De cualquier manera, sí lo hice. Lo describí y son las interacciones EF / DB las principales culpables.
Vaccano
@Anthony - ¿No es la materialización el tipo de cosas que se ejecutan primero? Si es así, tienes razón en que es muy lento. La primera carrera es super lenta. Pero como indiqué, no estoy demasiado preocupado por eso. El problema es el rendimiento total. (Si eso no es lo que es Materialización, entonces necesito investigar un poco para ver si es la causa de mi problema)
Vaccano
1
@Vaccano, no, la materialización es el proceso de tomar los datos de la base de datos y crear instancias y poblar el gráfico de objetos para representar esos datos. No estoy hablando del rendimiento de la primera ejecución, ya que el código está alterado (o incluso cuando Sql Server podría crear el plan de ejecución de la consulta), sino de lo que sucede cada vez que obtiene datos en forma de objetos.
Anthony Pegram

Respuestas:

46

Debe comenzar por perfilar los comandos SQL realmente emitidos por Entity Framework. Dependiendo de su configuración (POCO, entidades de seguimiento automático) hay mucho espacio para optimizaciones. Puede depurar los comandos SQL (que no deberían diferir entre el modo de depuración y el de liberación) utilizando el ObjectSet<T>.ToTraceString()método. Si encuentra una consulta que requiere una mayor optimización, puede usar algunas proyecciones para brindarle a EF más información sobre lo que está tratando de lograr.

Ejemplo:

Product product = db.Products.SingleOrDefault(p => p.Id == 10);
// executes SELECT * FROM Products WHERE Id = 10

ProductDto dto = new ProductDto();
foreach (Category category in product.Categories)
// executes SELECT * FROM Categories WHERE ProductId = 10
{
    dto.Categories.Add(new CategoryDto { Name = category.Name });
}

Podría ser reemplazado por:

var query = from p in db.Products
            where p.Id == 10
            select new
            {
                p.Name,
                Categories = from c in p.Categories select c.Name
            };
ProductDto dto = new ProductDto();
foreach (var categoryName in query.Single().Categories)
// Executes SELECT p.Id, c.Name FROM Products as p, Categories as c WHERE p.Id = 10 AND p.Id = c.ProductId
{
    dto.Categories.Add(new CategoryDto { Name = categoryName });
}

Acabo de escribir eso de mi cabeza, por lo que no es exactamente cómo se ejecutaría, pero EF en realidad hace algunas optimizaciones agradables si le dice todo lo que sabe sobre la consulta (en este caso, que necesitaremos la categoría- nombres). Pero esto no es como una carga ansiosa (db.Products.Include ("Categorías")) porque las proyecciones pueden reducir aún más la cantidad de datos a cargar.

J. Tihon
fuente
40
Esta respuesta parece razonable, hasta que se dé cuenta de que los tipos anónimos no son accesibles fuera del método en el que están definidos. Si desea cargar un objeto complejo y no escribir un megamoth, entonces necesita deserializar sus nuevos tipos anónimos en algún tipo de POCO. Una vez más, eso casi suena razonable hasta que se da cuenta de que al hacerlo, esencialmente ha REESCRITO SU PROPIO MARCO DE ENTIDAD. Lo cual es una mierda.
Doug
5
esto resultó en un aumento de 15x-20x en la velocidad para mí.
Dave Cousineau
11
Respuesta interesante y útil, todavía válida bastante tiempo después. @Doug: Lo cual no es una tontería ya que solo optimizas (usando proyecciones) esas pocas consultas en las que realmente necesitas usar el beneficio adicional. EF y POCO te dan un valor predeterminado razonable, ¡lo cual es muy bueno!
Victor
2
@Doug La mayoría de las aplicaciones tienen modelos de vista para escenarios de solo vista, ¿verdad? También podría hacer la mayor parte del mapeo mientras extrae los datos.
Casey
4
Solía ​​sentir que los ORM eran el futuro. Simplemente tenían sentido, hasta que comencé a usarlos. Entonces encontré a Dapper . Ahora, cuando veo soluciones como esta, me estremezco al ver cómo la complejidad aumenta rápidamente. Escribir SQL abstracto en C # no es una forma de vivir.
Michael Silver
80

El hecho es que productos como Entity Framework SIEMPRE serán lentos e ineficientes, porque están ejecutando mucho más código.

También me parece una tontería que la gente sugiera que uno debe optimizar las consultas LINQ, mirar el SQL generado, usar depuradores, precompilar, tomar muchos pasos adicionales, etc., es decir, perder mucho tiempo. Nadie dice: ¡Simplifique! Todos quieren complicar las cosas aún más dando aún más pasos (perdiendo el tiempo).

Un enfoque de sentido común sería no usar EF o LINQ en absoluto. Utilice SQL simple. No tiene nada de malo. El hecho de que haya una mentalidad de manada entre los programadores y sientan la necesidad de utilizar todos los productos nuevos que existen no significa que sea bueno o que funcione. La mayoría de los programadores piensan que si incorporan cada nuevo código publicado por una gran empresa, los convierte en programadores más inteligentes; no es cierto del todo. La programación inteligente se trata principalmente de cómo hacer más con menos dolores de cabeza, incertidumbres y en el menor tiempo posible. ¡Tiempo de recordar! Ese es el elemento más importante, así que trate de encontrar formas de no desperdiciarlo resolviendo problemas en un código incorrecto / inflado escrito simplemente para ajustarse a algunos extraños llamados 'patrones'

Relájese, disfrute de la vida, tómese un descanso de la codificación y deje de usar funciones, códigos, productos y 'patrones' adicionales. La vida es corta y la vida de su código es aún más corta, y ciertamente no es ciencia espacial. Elimine capas como LINQ, EF y otras, y su código se ejecutará de manera eficiente, se escalará y, sí, seguirá siendo fácil de mantener. Demasiada abstracción es un mal "patrón".

Y esa es la solución a tu problema.

Sean
fuente
155
Esto es tirar al bebé con el agua del baño. Optimiza los cuellos de botella, es tonto descartar EF porque es demasiado lento en algunos lugares, mientras que es bastante rápido en la mayoría de los demás. ¿Por qué no usar ambos? EF maneja bien los procedimientos almacenados y SQL sin procesar. Acabo de convertir una consulta LINQ-to-SQL que tomó más de 10 segundos en un SP que toma ~ 1 segundo, pero no voy a descartar todo LINQ-to-SQL. Ahorró MUCHO tiempo en otros casos más simples, con menos código y menos margen de error y las consultas son verificadas por el compilador y coinciden con la base de datos. Menos código significa un mantenimiento más sencillo y menos espacio para errores.
JulianR
11
En general, su consejo es bueno, pero no creo que sea correcto abandonar EF u otras abstracciones porque no funcionan bien el 10% del tiempo.
JulianR
49
SQL simple = ¿fácil de mantener? Simplemente no es cierto para aplicaciones muy grandes con mucha lógica empresarial. Escribir SQL complejo y reutilizable no es una tarea fácil. Personalmente, he tenido algunos problemas de rendimiento con EF, pero estos problemas simplemente no se comparan con los beneficios de un ORM adecuado en términos de un RAD y mantener las cosas SECAS (si hay algún nivel de complejidad involucrado).
MemeDeveloper
13
+ 10 ^ 100 Demasiada abstracción es un 'patrón'
malo
57
-1. "EF SIEMPRE será lento e ineficiente". No veo por qué afirmarías que algo como esto es la verdad absoluta. Tener más capas para atravesar hará que algo sea más lento, pero si esa diferencia es siquiera OBSERVABLE depende completamente de la situación, como la cantidad de datos y el tipo de consulta que se está ejecutando. Para mí, esto es lo mismo que decir 'C # será SIEMPRE lento e ineficiente' porque es una abstracción más alta que C ++. Sin embargo, muchas personas optan por utilizarlo porque las ganancias de productividad superan con creces la pérdida de rendimiento (si existe). Lo mismo se aplica a EF
Despertar
37

Una sugerencia es usar LINQ to Entity Framework solo para declaraciones CRUD de registro único.

Para consultas, búsquedas, informes, etc. más complejos, escriba un procedimiento almacenado y agréguelo al modelo de Entity Framework como se describe en MSDN .

Este es el enfoque que he adoptado con un par de mis sitios y parece ser un buen compromiso entre productividad y rendimiento. Entity Framework no siempre generará el SQL más eficiente para la tarea en cuestión. Y en lugar de perder el tiempo para averiguar por qué, escribir un procedimiento almacenado para las consultas más complejas en realidad me ahorra tiempo. Una vez que esté familiarizado con el proceso, no es demasiado complicado agregar procesos almacenados a su modelo EF. Y, por supuesto, el beneficio de agregarlo a su modelo es que obtiene toda esa bondad fuertemente tipada que proviene del uso de un ORM.

Steve Wortham
fuente
¿Tiene una idea sobre los métodos utilizados en scaffolding como db.athlete.find (id), etc. ¿Cómo funcionan en comparación con ADO.NET o dapper?
Es una trampa
15

Si está puramente traer los datos, que es una gran ayuda para el rendimiento cuando se dice EF para no realizar un seguimiento de las entidades que descarge. Haga esto usando MergeOption.NoTracking. EF solo generará la consulta, la ejecutará y deserializará los resultados a los objetos, pero no intentará realizar un seguimiento de los cambios de entidad ni nada de esa naturaleza. Si una consulta es simple (no pasa mucho tiempo esperando que la base de datos regrese), descubrí que configurarla en NoTracking puede duplicar el rendimiento de la consulta.

Consulte este artículo de MSDN sobre la enumeración MergeOption:

Resolución de identidad, gestión de estado y seguimiento de cambios

Este parece ser un buen artículo sobre el rendimiento de EF:

Desempeño y Entity Framework

JulianR
fuente
9
Antes de que alguien haga esto, sería una buena idea leer aquí. stackoverflow.com/questions/9259480/…
leen3o
6

Dice que ha perfilado la aplicación. ¿También ha perfilado el ORM? Hay un generador de perfiles EF de Ayende que resaltará dónde puede optimizar su código EF. Lo puedes encontrar aquí:

http://efprof.com/

Recuerde que puede usar un enfoque SQL tradicional junto con su ORM si lo necesita para obtener rendimiento.

¿Si hay un ORM más rápido / mejor? Dependiendo de su modelo de objeto / datos, podría considerar el uso de uno de los micro-ORM, como Dapper , Massive o PetaPoco .

El sitio de Dapper publica algunos puntos de referencia comparativos que le darán una idea de cómo se comparan con otros ORM. Pero vale la pena señalar que los micro-ORM no son compatibles con el rico conjunto de funciones de los ORM completos como EF y NH.

Es posible que desee echar un vistazo a RavenDB . Esta es una base de datos no relacional (de Ayende nuevamente) que le permite almacenar POCO directamente sin necesidad de mapeo . RavenDB está optimizado para lecturas y hace la vida de los desarrolladores mucho más fácil al eliminar la necesidad de manipular el esquema y mapear sus objetos a ese esquema. Sin embargo, tenga en cuenta que este es un enfoque significativamente diferente al uso de un enfoque ORM y estos se describen en el sitio del producto .

Sean Kearon
fuente
3

He encontrado la respuesta de @Slauma aquí muy útil para acelerar las cosas. Usé el mismo tipo de patrón para inserciones y actualizaciones, y el rendimiento se disparó.

MemeDesarrollador
fuente
2

Desde mi experiencia, el problema no es con EF, sino con el enfoque ORM en sí.

En general, todos los ORM sufren de problemas N + 1 , no consultas optimizadas, etc. Mi mejor suposición sería rastrear las consultas que causan degradación del rendimiento e intentar ajustar la herramienta ORM, o reescribir esas partes con SPROC.

Valera Kolupaev
fuente
1
La gente me sigue diciendo esto. Pero estableceré una declaración de selección simple usando ADO de la vieja escuela, y la misma selección simple usando un contexto EF y EF siempre es considerablemente más lenta. Realmente quiero que me guste EF, pero sigue haciendo la vida más difícil en lugar de más fácil.
Sinaesthetic
1
@Sinaesthetic Por supuesto que es más lento. Del mismo modo, el código escrito con Linq to Objects suele ser más lento que el código escrito sin él. La pregunta no es realmente si es más rápido o tan rápido (¿cómo podría ser, cuando bajo el capó todavía tiene que emitir la consulta que estaba emitiendo a mano?) Sino si 1) todavía es lo suficientemente rápido para sus necesidades 2) ahorra tiempo escribiendo el código 3) los beneficios compensan los costos. Basado en esos elementos, creo que EF es apropiado para muchos proyectos.
Casey
@Sinaesthetic También agregaría que si no usa un ORM, lo que sucede la mayoría de las veces no es que cada consulta SQL esté ajustada y optimizada, sino que la aplicación termina desarrollando un proceso interno, orgánico, deficiente. - ORM respaldado y de bajo rendimiento, a menos que su equipo sea excepcionalmente disciplinado y muy preocupado por el rendimiento.
Casey
1

También me encontré con este problema. Odio volcar en EF porque funciona muy bien, pero es lento. En la mayoría de los casos, solo quiero encontrar un registro o actualizar / insertar. Incluso operaciones simples como esta son lentas. Retiré 1100 registros de una tabla a una lista y esa operación tomó 6 segundos con EF. Para mí esto es demasiado tiempo, incluso ahorrar lleva demasiado tiempo.

Terminé haciendo mi propio ORM. Saqué los mismos 1100 registros de una base de datos y mi ORM tomó 2 segundos, mucho más rápido que EF. Todo con mi ORM es casi instantáneo. La única limitación en este momento es que solo funciona con MS SQL Server, pero podría cambiarse para trabajar con otros como Oracle. Utilizo MS SQL Server para todo ahora mismo.

Si desea probar mi ORM, aquí está el enlace y el sitio web:

https://github.com/jdemeuse1204/OR-M-Data-Entities

O si quieres usar nugget:

PM> Paquete de instalación OR-M_DataEntities

La documentación también está ahí

TheMiddleMan
fuente
0

Solo tiene sentido optimizar después de haber perfilado. Si descubre que el acceso a la base de datos es lento, puede realizar la conversión para usar procedimientos almacenados y mantener EF. Si descubre que el EF en sí es lento, es posible que deba cambiar a un ORM diferente o no usar un ORM en absoluto.

Gabe
fuente
0

Tenemos una aplicación similar (Wcf -> EF -> base de datos) que realiza 120 solicitudes por segundo fácilmente, por lo que estoy más que seguro de que EF no es su problema aquí, dicho esto, he visto importantes mejoras de rendimiento con consultas compiladas.

np-hard
fuente
El 98% de mi código son llamadas de creación y actualización. No sé si eso marca la diferencia, pero es mucho más lento que 120 por segundo.
Vaccano
sí, esa no sería una aplicación típica, le sugiero que perfile su aplicación. para nosotros su mayor parte lee ...
np-hard
0

Usé EF, LINQ to SQL y apuesto. Dapper es el más rápido. Ejemplo: necesitaba 1000 registros principales con 4 registros secundarios cada uno. Usé LINQ to sql, me tomó unos 6 segundos. Luego cambié a Dapper, recuperé 2 conjuntos de registros del único procedimiento almacenado y para cada registro agregué los registros secundarios. Tiempo total 1 segundo.

Además, el procedimiento almacenado usó funciones de valor de tabla con aplicación cruzada, encontré que las funciones de valor escalar son muy lentas.

Mi consejo sería usar EF o LINQ to SQL y, para ciertas situaciones, cambiar a apuesto.

tfa
fuente
-1

Entity Framework no debería causar grandes cuellos de botella en sí mismo. Lo más probable es que existan otras causas. Puede intentar cambiar EF a Linq2SQL, ambos tienen funciones de comparación y el código debería ser fácil de convertir, pero en muchos casos Linq2SQL es más rápido que EF.

Wiktor Zychla
fuente