实体框架太慢。我有什么选择?[关闭]


93

我遵循了“不要过早优化”的口头禅,并使用实体框架对WCF服务进行了编码。

但是,我分析了性能,并且实体框架太慢了。(我的应用程序在大约1.2秒内处理了2条消息,而我正在重写的(旧版)应用程序同时执行5-6条消息。(旧版应用程序为其数据库访问调用了sprocs。)

我的分析指向Entity Framework,它占用了每条消息的大部分时间。

那么,我有什么选择?

  • 有更好的ORM吗?
    (仅支持正常读取和写入对象并快速完成的操作。)

  • 有没有办法使实体框架更快?
    注意:当我说更快时,我的意思是从长远来看,而不是第一次通话。的消息。)

  • 某些神秘的第三种选择,将帮助我提高服务速度。

注意:我的大多数数据库交互都是“创建和更新”。我很少选择和删除。


这听起来像是对“ linq缓慢”的重述,您怎么知道它是EF?您是否已描述所有更改?
Maess 2011年

6
一些答案指向查询。以我的经验,EF的缓慢与查询无关,而与实现的成本有关,而这些成本通常与变更跟踪及其对创建实例的影响有关。不幸的是,我没有为您准备的灵丹妙药,所以这只是一条评论,但我建议您查看性能分析是否显示出较高的实现成本,如果是,则研究可以做些什么。
安东尼·佩格拉姆

@Maess-我以为我表示已进行了概要分析,发现EF / DB运行缓慢。无论哪种方式,是的,我做到了。我对它进行了分析,而造成这种现象的主要原因是EF / DB交互。
瓦卡诺2011年

@Anthony-实现不是首先运行的东西吗?如果是这样,您说得很慢,这是正确的。第一次运行非常慢。但是,正如我指出的那样,我对此不太担心。问题是总吞吐量。(如果这不是实现的原因,那么我需要进行一些研究,看看是否是造成问题的原因)
Vaccano 2011年

1
@Vaccano,不,实现是从数据库中获取数据并实例化并填充对象图以表示该数据的过程。我不是在讨论代码被弄乱(或者甚至是Sql Server可能创建查询执行计划)时的首次运行性能,而是每次以对象形式获取数据时会发生什么。
安东尼·佩格拉姆

Answers:


46

您应该首先分析由实体框架实际发出的SQL命令。根据您的配置(POCO,自我跟踪实体),还有很大的优化空间。您可以使用ObjectSet<T>.ToTraceString()方法来调试SQL命令(在调试和发布模式之间不应有区别)。如果遇到需要进一步优化的查询,则可以使用一些预测为EF提供有关您要完成的工作的更多信息。

例:

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 });
}

可以替换为:

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 });
}

我只是在脑子里打了一下,所以这并不是它的执行方式,但是如果您将查询的所有知识告诉EF,EF实际上会做一些不错的优化(在这种情况下,我们将需要以下类别:名称)。但这不像急切加载(db.Products.Include(“ Categories”)),因为投影可以进一步减少要加载的数据量。


40
这个响应听起来很合理,直到您意识到匿名类型在定义它们的方法之外是不可访问的。如果您想加载一个复杂的对象而不是编写一个复杂的对象,则需要将新的匿名类型反序列化为某种POCO。同样,这听起来似乎很合理,直到您意识到这样做实际上已经重写了您自己的实体框架。这是胡扯。
道格

5
这使我的速度提高了15到20倍。
Dave Cousineau 2013年

11
有趣而有用的回复,仍然会在一段时间后生效。@Doug:这不是真的胡说八道,因为您只优化(使用投影)您真正需要的少数查询就可以使用额外的好处。EF和POCO为您提供合理的默认值,这非常好!
维克多

2
@Doug大多数应用程序都具有仅视图场景的视图模型,对吗?拔出数据时,也可能会进行尽可能多的映射。
Casey 2015年

4
我曾经觉得ORM就是未来。直到我开始使用它们之前,它们才有意义。然后我找到了Dapper。现在,当看到这样的解决方案时,我想知道复杂性如何迅速提高。用C#编写抽象的SQL是不可能的。
Michael Silver

80

事实是,诸如Entity Framework之类的产品将始终运行缓慢且效率低下,因为它们正在执行更多的代码。

我还觉得很愚蠢,有人建议人们应该优化LINQ查询,查看生成的SQL,使用调试器,预编译,采取许多额外的步骤,等等,即浪费大量时间。没有人说-简化!每个人都想通过采取更多步骤(浪费时间)进一步使事情复杂化。

常识性方法是根本不使用EF或LINQ。使用普通的SQL。没有什么问题。仅仅因为程序员之间存在羊群的心态,并且他们感到有使用那里的每个新产品的冲动,但这并不意味着它是好的,否则它将起作用。大多数程序员认为,如果他们合并了一家大公司发布的所有新代码,就会使他们成为一个更聪明的程序员。完全不对。智能编程主要是关于如何以更少的头痛,不确定性和最少的时间做更多的事情。记住-时间!这是最重要的元素,因此请尝试寻找方法,不要将其浪费在解决不好/过大的代码中的问题上,这些代码只是为了符合某些奇怪的所谓“模式”而编写

放松,享受生活,从编码中休息一下,并停止使用其他功能,代码,产品和“模式”。寿命很短,代码的寿命甚至更短,这当然不是火箭科学。删除LINQ,EF等图层,您的代码将高效运行,可扩展,是的,它仍然易于维护。过多的抽象是不好的“模式”。

这就是解决您的问题的方法。


155
这把婴儿和洗澡水一起扔了出去。您可以优化瓶颈,将EF丢掉是很愚蠢的,因为在某些地方它太慢了,而在其他大多数地方却很快。为什么不同时使用两者?EF可以很好地处理存储过程和原始SQL。我刚刚将耗时10秒以上的LINQ-to-SQL查询转换为一个耗时约1秒钟的SP,但是我不会将所有LINQ-to-SQL淘汰掉。在其他更简单的情况下,它节省了很多时间,更少的代码和更少的错误空间,并且查询已通过编译器验证并与数据库匹配。更少的代码更易于维护,并且更少的出错空间。
JulianR 2011年

11
总体而言,您的建议虽然不错,但我认为放弃EF或其他抽象并不正确,因为它们在10%的时间内效果不佳。
JulianR 2011年

49
普通SQL =易于维护?对于具有大量业务逻辑的超大型应用程序而言,情况并非如此。编写复杂的可重用SQL并非易事。就我个人而言,我在EF方面遇到了一些性能问题,但是这些问题根本无法与在RAD方面保持适当的ORM和使事物保持干燥(如果涉及到任何复杂程度)相比,这些问题无法相比。
MemeDeveloper 2012年

13
+ 10 ^ 100太多的抽象是一个糟糕的“模式”
Makach

57
-1。“ EF总是会缓慢而低效的。” 我不明白为什么你会这样断言是绝对的事实。有更多的层次需要经过,这会使速度变慢,但是这种差异是否甚至是NOTICEABLE完全取决于诸如数据量和正在执行的查询类型之类的情况。对我来说,这就像说“ C#总是会很慢且效率低下”一样,因为它比C ++具有更高的抽象度。然而,许多人选择使用它是因为生产率的提高远远超过了性能损失(如果有的话)。同样适用于EF
Despertar

37

一种建议是仅对单记录CRUD语句使用LINQ to Entity Framework。

对于更多涉及的查询,搜索,报告等,请编写存储过程并将其添加到MSDN上描述的Entity Framework模型中。

这是我在几个站点上采用的方法,似乎在生产率和性能之间取得了很好的折衷。实体框架不会总是为手头的任务生成最有效的SQL。与其花时间找出原因,不如为更复杂的查询编写存储过程实际上为我节省了时间。一旦您熟悉了该过程,就可以轻松地将存储的过程添加到EF模型中。当然,将其添加到模型中的好处是,您可以获得使用ORM带来的所有强类型优势。


您对db.athlete.find(id)等支架中使用的方法有什么想法吗?与ADO.NET或dapper相比,它们的性能如何?
这是一个陷阱

15

如果您纯粹是在获取数据,则当您告诉EF不要跟踪其获取的实体时,这对性能有很大帮助。通过使用MergeOption.NoTracking执行此操作。EF只会生成查询,执行查询并将结果反序列化为对象,但不会尝试跟踪实体更改或任何此类性质。如果查询很简单(不用花很多时间等待数据库返回),我发现将其设置为NoTracking可以使查询性能提高一倍。

请参阅MergeOption枚举上的此MSDN文章:

身份解析,状态管理和变更跟踪

这似乎是有关EF性能的好文章:

绩效与实体框架


9
在任何人进行此操作之前,在这里阅读可能是一个好主意。stackoverflow.com/questions/9259480/...
leen3o

6

您说您已对应用程序进行了概要分析。您是否也介绍了ORM?Ayende提供了一个EF探查器,该探查器将突出显示可以优化EF代码的位置。你可以在这里找到它:

http://efprof.com/

请记住,如果需要提高性能,则可以在ORM中使用传统的SQL方法。

是否有更快/更好的ORM?根据您的对象/数据模型,您可以考虑使用一种微型ORM,例如DapperMassivePetaPoco

Dapper站点发布了一些比较基准,这些基准将使您了解它们与其他ORM的比较方式。但是,值得注意的是,微型ORM不支持像EF和NH这样的完整ORM的丰富功能集。

您可能想看看RavenDB。这是一个非关系数据库(再次来自Ayende),可让您直接存储POCO,而无需映射。RavenDB针对读取进行了优化,并且通过消除操作架构并将对象映射到该架构的需求,使开发人员的工作变得更加轻松。但是,请注意,这是与使用ORM方法完全不同的方法,这些方法在产品站点中概述。


3

我发现这里 @Slauma的答案对于加快速度非常有用。对于插入和更新,我使用了相同的模式-性能得到了提高。


2

根据我的经验,问题不在于EF,而在于ORM方法本身。

通常,所有ORM都会遇到N + 1问题,而不是优化查询等问题。我的最佳猜测是跟踪导致性能下降的查询,并尝试调优ORM工具,或使用SPROC重写该零件。


1
人们不断告诉我这一点。但是,我将使用旧式ADO设置一个简单的select语句,而使用EF上下文和EF进行相同的简单选择总是相当慢。我真的很想喜欢EF,但是它使生活变得更艰难而不是更轻松。
Sinaesthetic,2014年

1
@Sinaesthetic当然慢了。同样,使用Linq编写到对象的代码通常比没有编写代码的代码要慢。现在的问题是不是真的不管是快更快,(这怎么可能,当引擎盖下它仍然发出查询,你用手发行?),但无论是1)它仍然不够快为您的需求2)节省您花时间编写代码3)好处抵消了费用。基于这些项目,我认为EF适用于许多项目。
Casey

@Sinaesthetic我还要补充一点,如果您不使用ORM,那么经常发生的事情不是不是每个SQL查询都经过了微调和优化,而是应用程序最终开发了一个内部的,有机的,性能较差的应用程序支持的ORM表现不佳,除非您的团队纪律严明并且非常关注性能。
Casey


1

我也遇到了这个问题。我讨厌放弃EF,因为它运行得很好,但是速度很慢。在大多数情况下,我只想查找记录或更新/插入。像这样的简单操作也很慢。我从一个表中将1100条记录拉回到一个列表中,而使用EF则花费了6秒钟。对我来说,这太长了,甚至储蓄也花了太长时间。

我最终制作了自己的ORM。我从数据库中提取了相同的1100条记录,我的ORM用了2秒,比EF快得多。我的ORM的一切几乎都是即时的。现在唯一的限制是它仅适用于MS SQL Server,但可以更改为与Oracle等其他产品一起使用。我现在使用MS SQL Server进行所有操作。

如果您想尝试我的ORM,请访问以下链接和网站:

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

或者,如果您想使用块金:

PM>安装包OR-M_DataEntities

文档也在那里


0

剖析后进行优化才有意义。如果发现数据库访问缓慢,则可以转换为使用存储过程并保留EF。如果发现EF本身很慢,则可能不得不切换到其他ORM或根本不使用ORM。


0

我们有一个类似的应用程序(Wcf-> EF->数据库),可以轻松地每秒处理120个请求,因此,我非常确定EF不是您的问题,也就是说,通过编译查询,我已经看到了重大的性能改进。


我的代码中98%是创建和更新调用。我不知道这有什么区别,但是它比每秒120个慢得多。
瓦卡诺2011年

是的,那不是典型的应用程序,我建议您分析您的应用程序。对我们来说,这主要是读...
np-hard

0

我使用EF,LINQ to SQL和dapper。Dapper是最快的。示例:我需要1000条主记录,每条记录有4个子记录。我使用LINQ到sql,大约花了6秒钟。然后,我切换到dapper,从单个存储过程中检索了2个记录集,并为每个记录添加了子记录。总时间为1秒。

同样,存储过程使用了表值函数与交叉应用,我发现标量值函数非常慢。

我的建议是使用EF或LINQ to SQL,并在某些情况下切换到dapper。


-1

实体框架本身不应引起重大瓶颈。可能还有其他原因。您可以尝试将EF切换到Linq2SQL,它们都具有比较功能,并且代码应该易于转换,但是在许多情况下,Linq2SQL比EF快。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.