首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >实体框架5性能问题

实体框架5性能问题
EN

Stack Overflow用户
提问于 2013-05-24 19:04:28
回答 4查看 7.6K关注 0票数 5

现在我正在处理一个相当复杂的数据库。我们的对象模型被设计成映射到数据库。我们在POCO类中使用EF 5,手动生成。

一切正常,但有人抱怨演出。我从来没有在EF的性能问题,所以我想知道,这一次,我只是做了什么可怕的错误,或问题可能出现在其他地方。

主查询可以由动态参数组成。我有几个if和switch块在概念上是这样的:

代码语言:javascript
复制
if (parameter != null) { query = query.Where(c => c.Field == parameter); }

另外,对于一些复杂和/或组合,我使用来自Albahari的LinqKit扩展。

这个查询针对的是一个大的“订单”表,其中包含了数年和几年的数据。平均使用是一个2个月的范围过滤器。

现在,当主查询被合成时,它会被一个Skip/Take组合分页,其中采取的设置为10个元素。

在所有这些之后,IQueryable通过层发送,到达使用Automapper的MVC层。

在这里,当Automapper开始迭代(因此真正执行了查询)时,它调用了一组导航属性,这些属性具有自己的导航属性等等。根据EF建议,所有内容都被设置为延迟加载,如果您有超过3或4个不同的实体要包含,就不要急于加载。我的场景是这样的:

  • 订单(最多10)
    • 命令下的多个导航属性
      • 其中有些有其他导航(本地化实体)。

代码语言:javascript
复制
- Order details (many order details per order) 
    - Many navigation properties under each Order detail 
        - Some of these have other navigation under them (localization entities)

这很容易导致对单个呈现的“页面”进行总计的300+查询。这些查询都非常快,只需几毫秒就能运行,但仍然有两个主要问题:

  • 延迟加载的属性是按顺序调用的,而不是并行化的,因此需要更多的时间。
  • 由于前面的一点,每个查询之间都有一些死时间,因为数据库必须对每个查询接收sql、运行它、返回它等等。

为了了解它是如何进行的,我试着用急切的加载来进行相同的查询,正如我预测的那样,这是一个完全的灾难,翻译的sql超过7K行(是的,有7000行),而且总体速度要慢得多。

现在,我不愿意认为EF和Linq不是这种情况下的正确选择。有些人说,如果他们编写一个存储过程来获取所有所需的数据,它的运行速度会快几十倍。我不相信这是真的,我们将失去所有相关实体的自动物化。

我想到了一些可以改进的事情,比如:

  • 表拆分以减少所选列
  • 关闭对象跟踪,因为此场景为只读(有未跟踪的实体)

所有这些,主要的抱怨是结果页面(在MVC 4中完成)呈现得太慢,经过一些诊断之后,似乎所有的服务器时间都是“服务器时间”,而不是“网络时间”,大约需要8到12秒的服务器时间。

根据我的经验,这是不应该发生的。我想知道我是否以一种错误的方式来处理这个查询需求,或者我必须将我的注意力转向其他的东西(可能是配置不好的IIS服务器,或者任何其他我真的不知道的东西)。针头说,数据库有它的索引好,我们的dba非常仔细地检查。

因此,如果有人有任何提示,建议,最佳实践,我错过了这一点,或只是可以告诉我,我是绝对错误的使用EF与延迟加载这个场景.不客气。

EN

回答 4

Stack Overflow用户

发布于 2013-08-13 12:40:04

对于一个非常复杂的查询,它带来了大量的分层数据,如果您采取正确的方法,存储的procs通常不会帮助您在LINQ/EF上提高性能。正如您已经注意到的,在这种情况下,EF (延迟加载和急切加载)的两个“开箱即用”选项不能很好地工作。然而,仍然有几种很好的方法来优化这一点:

(1)与其将一堆实体读入内存,然后通过automapper进行映射,不如在可能的情况下直接在查询中执行“自动执行”。例如:

代码语言:javascript
复制
var mapped = myOrdersQuery.Select(o => new OrderInfo { Order = o, DetailCount = o.Details.Count, ... })
    // by deferring the load until here, we can bring only the information we actually need 
    // into memory with a single query
    .ToList();

如果您只需要复杂层次结构中字段的子集,则此方法非常有效。此外,如果您需要返回比平面表格数据更复杂的内容,EF选择分层数据的能力比使用存储的procs简单得多。

(2)手动运行多个LINQ查询,并在内存中组装结果。例如:

代码语言:javascript
复制
// read with AsNoTracking() since we'll be manually setting associations
var myOrders = myOrdersQuery.AsNoTracking().ToList();
var orderIds = myOrders.Select(o => o.Id);
var myDetails = context.Details.Where(d => orderIds.Contains(d.OrderId)).ToLookup(d => d.OrderId);
// reassemble in memory
myOrders.ForEach(o => o.Details = myDetails[o.Id].ToList());

当您需要所有的数据并且仍然希望利用尽可能多的EF物化时,这是非常有效的。注意,在大多数情况下,存储的proc方法不能做得更好(它使用的是原始SQL,因此它必须运行多个表查询),但不能重用您已经用LINQ编写的逻辑。

(3)使用Include()手动控制哪些关联需要加载。这可以与#2结合起来,以利用某些关联的EF加载,同时给您手动加载其他关联的灵活性。

票数 4
EN

Stack Overflow用户

发布于 2013-05-24 19:13:43

尝试考虑一个高效而简单的sql查询,以便为您的视图获取数据。

有可能吗?

如果没有,请尝试对表进行分解(去denormalize),以便减少获取数据所需的联接。此外,表列上是否有有效的索引来加速数据检索?

如果是,忘记EF,编写一个存储过程并使用它获取数据。

对于选定的查询,关闭跟踪对于只读场景来说是必须的.看看我的数字:

15.html

正如您所看到的,跟踪和不跟踪场景之间的差别是很大的。

我会尝试急切地加载,但不是所有地方(因此,您不会以7k行长查询结束),而是在选定的子查询中。

票数 2
EN

Stack Overflow用户

发布于 2013-05-24 19:12:41

要考虑的一点是,EF无疑有助于使开发时间更快。但是,您必须记住,当您从DB返回大量数据时,EF使用的是动态SQL。这意味着EF必须1.创建SQL,2. Server然后需要创建一个执行计划。这在运行查询之前发生。

当使用存储过程时,Server可以缓存执行计划(可以为性能进行编辑),这确实比使用EF更快。但是..。您可以始终创建存储的proc,然后从EF执行它。任何复杂的过程或查询,我将转换为存储过程,然后从EF调用。然后,您可以看到您的性能增益和重新评估从那里。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/16741956

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档