来自Entity Framework的极慢且低效的查询执行

时间:2015-03-24 20:41:07

标签: c# asp.net sql-server entity-framework

我已经在Windows 2008R2中的ASP.NET上运行了.NET 4.5的Entity Framework 4.1。我使用EF代码优先连接到SQL Server 2008R2,并执行相当复杂的LINQ查询,但只产生Count()

我在两个不同的Web服务器上重现了这个问题,但只有一个数据库(当然是生产)。它最近开始在Web或数据库端没有应用程序,数据库结构或服务器更改。

我的问题是在某些情况下执行查询需要花费大量时间(接近4分钟)。我可以从SQL Profiler中提取实际查询,并在大约1秒内在SSMS中执行。这对我来说是一致且可重复的,但是如果我将其中一个参数(“2015-01-22之后的日期”参数)的值更改为更早的内容,例如2015-01-01,或更晚的2015-02- 01,它在EF中工作正常。但我把它重新回到了2015-01-22并再次放缓。我可以一遍又一遍地重复这个。

然后我可以在EF中运行一个类似但不相关的查询,然后回到原始版本,这次运行正常 - 与以前完全相同的查询。但是如果我打开一个新的浏览器,循环就会重新开始。那部分也没有意义 - 我们没有做任何事情来保留用户会话中的数据上下文,所以我不知道为什么会发挥作用。

但这一切都告诉我数据本身很好。

在Profiler中,当查询正常运行时,大约需要一两秒钟,并显示大约2,000,000个读取和大约2,000个CPU。当它运行缓慢时,需要3.5分钟,值为300,000,000和200,000 - 所以读取大约高150倍,CPU高出100倍。同样,对于相同的SQL语句。

有关EF可能采取哪些不同方式的建议,这些建议不会出现在查询文本中?是否存在某种隐藏的连接属性,在某些情况下可能导致不同的执行计划?

修改

EF构建的查询是使用文本中包含的参数构建巨型字符串的查询之一,而不是SQL参数:

exec sp_executesql 
   N'SELECT [GroupBy1].[A1] AS [C1] 
     FROM ( 
          SELECT COUNT(1) AS [A1]
           ...
           AND ([Extent1].[Added_Time] >= convert(datetime2, ''2015-01-22 00:00:00.0000000'', 121)) 
           ...
           ) AS [GroupBy1]'

修改

我不会将此作为答案添加,因为它实际上并没有解决根本问题,但最终通过重建索引和重新计算统计数据来解决这个问题。没有比平时更长的时间,这似乎已经解决了造成这个问题的任何问题。

我会继续阅读这里的一些链接,以防再次发生这种情况,但由于现在一切正常并且无法再现,我不知道我是否确切知道它到底在做什么。< / p>

感谢所有想法。

5 个答案:

答案 0 :(得分:5)

我最近有一个非常类似的场景,一个查询会在数据库中直接执行它,但使用EF(版本5,在我的情况下)的性能很差。这不是一个网络问题,差异是从4毫秒到10分钟。

问题最终成为映射问题。我有一个映射到NVARCHAR的列,而它在数据库中是VARCHAR。似乎无害,但这导致了数据库中的隐式转换,这完全破坏了性能。

我不完全确定为什么会发生这种情况,但是根据我的测试,这导致数据库执行索引扫描而不是索引搜索,显然他们的表现非常不同。

comparison index scan and index seek

我在博客上发表了here(免责声明:它是葡萄牙语),但后来我发现Jimmy Bogard在2012年的post中描述了这个确切的问题,我建议你看一下。 / p>

由于你的查询中有转换,我会说从那里开始。仔细检查所有列映射,检查表格列与实体属性之间的差异。避免在查询中进行隐式转换 如果可以,请检查您的execution plan以找到任何不一致的地方,请注意黄色警告三角形,它可能表示有关进行隐式转换的问题:

query issue implicit conversion warning

我希望这对你有所帮助,这对我们来说是一个非常难以找到的问题,但最终还是有道理的。

答案 1 :(得分:0)

有一篇关于实体框架性能考虑的优秀文章here

我想提请您注意关于冷与热查询执行的部分:

  

第一次针对给定模型进行任何查询时,   实体框架在幕后加载和做了很多工作   验证模型。我们经常将此第一个查询称为   “冷”查询。针对已经加载的模型的进一步查询   被称为“温暖”的查询,速度更快。

在LINQ查询执行期间,“元数据加载”步骤对冷查询执行的性能有很大影响。但是,一旦加载的元数据将被缓存,未来的查询将运行得更快。元数据缓存在DbContext之外,只要应用程序池存在,就可以重复使用。

为了提高性能,请考虑以下操作:

  • 使用预先生成的视图
  • 使用查询计划缓存
  • 不使用跟踪查询(仅在以只读方式访问时)
  • 创建实体框架的本机图像(仅在使用EF 6或更高版本时才相关)

所有这些要点都在上面提供的链接中有详细记录。此外,您还可以找到有关创建实体框架here的本机映像的其他信息。

答案 2 :(得分:0)

对于为什么会发生这种情况,我没有具体的答案,但它肯定与查询的处理方式有关,而不是查询本身。如果您说从SSMS运行相同的生成查询没有任何问题,那么问题就不存在。

您可以尝试的解决方法:存储过程。 EF可以很好地处理它们,它是处理可能复杂或昂贵的查询的理想方式。

答案 3 :(得分:0)

只是把它放在那里,因为它没有作为一种可能性解决:

鉴于您正在使用实体框架(EF),如果您使用实体的延迟加载,则EF需要通过连接字符串启用多个活动结果集(MARS)。虽然看起来似乎完全不相关,但MARS有时会产生这种确切的行为,即在SSMS中快速运行的东西,但通过EF可以非常缓慢地(几秒钟变成几分钟)。

测试此方法的一种方法是关闭延迟加载并删除MultipleActiveResultSets=True;(默认为&#34; false&#34;)或至少将其更改为MultipleActiveResultSets=False;

据我所知,遗憾的是,这种行为没有解决方法或修复(目前)。

以下是此问题的一个实例:Same query with the same query plan takes ~10x longer when executed from ADO.NET vs. SMSS

答案 4 :(得分:-3)

意识到您正在使用Entity Framework 4.1,我建议您升级到Entity Framework 6.

性能提升很多,EF 6比EF 4.1快得多。

我在其他回复中提到的MSDN article about Entity Framework performance consideration也对EF 4.1和EF 6进行了比较。

结果可能需要进行一些重构,但性能的提升应该是值得的(这会同时减少技术债务)。