目前,我正在处理一个相当复杂的数据库。我们的对象模型旨在映射到数据库。我们使用带有手动生成的POCO类的EF 5。
一切都正常工作,但是有人抱怨性能不佳。我以前从未遇到过EF的性能问题,所以我想知道这次是否做错了什么,或者问题可能存在于其他地方。
主查询可能由动态参数组成。我有几个if和switch块,概念上类似于:
if (parameter != null) { query = query.Where(c => c.Field == parameter); }
此外,对于一些复杂的And/Or组合,我使用来自Albahari的LinqKit扩展。查询针对一个包含多年数据的大型"Orders"表格。平均使用情况是2个月的范围过滤器。
现在,当主查询被组合后,它将获得由Skip/Take组成的分页组合,其中Take设置为10个元素。
在所有这些之后,IQueryable通过各层发送,达到MVC层,Automapper在这里被使用。
在这里,当Automapper开始迭代(因此查询确实被执行)时,它调用了一堆导航属性,这些导航属性有自己的导航属性等等。根据EF的建议,所有东西都被设置为惰性加载,以避免贪婪地加载(如果您要包含3或4个不同的实体)。我的情况是这样的:
- 订单(最多10个)
- 订单下有许多导航属性 - 其中一些有其他导航(本地化实体) - 订单明细(每个订单有很多订单明细) - 每个订单明细下有许多导航属性 - 其中一些有其他导航(本地化实体)
这很容易导致单个“页面”上超过300个查询。每个查询非常快,运行几毫秒,但仍存在2个主要问题:
- 惰性加载属性是按顺序调用而不是并行化,因此需要更长的时间。 - 由于前一个点的结果,每个查询之间存在一些死时间,因为数据库必须接收SQL,运行它,返回它等等。
只是为了看看情况如何,我尝试使用贪婪加载进行相同的查询,正如我所预测的那样,这是一场灾难,翻译后的SQL超过7K行(是的,七千)且总体上更慢。
现在我不愿意认为EF和Linq不适合这种情况。有人说如果他们编写一个检索所有所需数据的存储过程,它将运行快十倍。我不相信那是真的,并且我们将失去自动实例化所有相关实体的特性。
我想到了一些可以改善的事情,例如:
- 表分割以减少选择的列。 - 关闭对象跟踪,因为此方案是只读的(具有未跟踪的实体)。
总之,主要投诉是结果页面(在MVC 4中完成)渲染太慢,在进行了一些诊断后,似乎全部是“服务器时间”,而不是“网络时间”,需要约8到12秒的服务器时间。
根据我的经验,这种情况不应该发生。我在想是否我以错误的方式处理了这个查询需求,或者我需要转向其他方面(也许是配置有问题的IIS服务器,或者其他一些我完全不了解的东西)。毋庸置疑,数据库已经通过我们的dba非常仔细地检查过它的索引。
所以,如果任何人对此有任何提示、建议、最佳实践我可能会错过,或者只是告诉我在这种情况下使用具有延迟加载功能的EF是错误的...欢迎大家提供帮助。