简体   繁体   English

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

[英]Extremely slow and inefficient query execution from Entity Framework

I've got Entity Framework 4.1 with .NET 4.5 running on ASP.NET in Windows 2008R2. 我已经在Windows 2008R2中的ASP.NET上运行了.NET 4.5的Entity Framework 4.1。 I'm using EF code-first to connect to SQL Server 2008R2, and executing a fairly complex LINQ query, but resulting in just a Count() . 我使用EF代码优先连接到SQL Server 2008R2,并执行相当复杂的LINQ查询,但只产生一个Count()

I've reproduced the problem on two different web servers but only one database (production of course). 我在两个不同的Web服务器上重现了这个问题,但只有一个数据库(当然是生产)。 It recently started happening with no application, database structure, or server changes on the web or database side. 它最近开始在Web或数据库端没有应用程序,数据库结构或服务器更改。

My problem is that executing the query under certain circumstances takes a ridiculous amount of time (close to 4 minutes). 我的问题是在某些情况下执行查询需要花费大量时间(接近4分钟)。 I can take the actual query, pulled from SQL Profiler, and execute in SSMS in about 1 second. 我可以从SQL Profiler中提取实际查询,并在大约1秒内在SSMS中执行。 This is consistent and reproducible for me, but if I change the value of one of the parameters (a "Date after 2015-01-22" parameter) to something earlier, like 2015-01-01, or later like 2015-02-01, it works fine in EF. 这对我来说是一致且可重复的,但是如果我将其中一个参数(“2015-01-22之后的日期”参数)的值更改为更早的内容,例如2015-01-01,或更晚的2015-02- 01,它在EF中工作正常。 But I put it back to 2015-01-22 and it's slow again. 但我把它重新回到了2015-01-22并再次放缓。 I can repeat this over and over again. 我可以一遍又一遍地重复这个。

I can then run a similar but unrelated query in EF, then come back to the original, and it runs fine this time - same exact query as before. 然后我可以在EF中运行一个类似但不相关的查询,然后回到原始版本,这次运行正常 - 与之前完全相同的查询。 But if I open a new browser, the cycle starts over again. 但是如果我打开一个新的浏览器,循环就会重新开始。 That part also makes no sense - we're not doing anything to retain the data context in a user session, so I have no clue whatsoever why that comes into play. 那部分也没有意义 - 我们没有做任何事情来保留用户会话中的数据上下文,所以我不知道为什么会发挥作用。

But this all tells me that the data itself is fine. 但这一切都告诉我数据本身很好。

In Profiler, when the query runs properly, it takes about a second or two, and shows about 2,000,000 in reads and about 2,000 in CPU. 在Profiler中,当查询正常运行时,大约需要一两秒钟,并显示大约2,000,000个读取和大约2,000个CPU。 When it runs slowly, it takes 3.5 minutes, and the values are 300,000,000 and 200,000 - so reads are about 150 times higher and CPU is 100 times higher. 当它运行缓慢时,需要3.5分钟,值为300,000,000和200,000 - 所以读取大约高150倍,CPU高出100倍。 Again, for the identical SQL statement. 同样,对于相同的SQL语句。

Any suggestions on what EF might be doing differently that wouldn't show up in the query text? 有关EF可能采取哪些不同措施的建议,这些建议不会出现在查询文本中? Is there some kind of hidden connection property which might cause a different execution plan in certain circumstances? 是否存在某种隐藏的连接属性,在某些情况下可能导致不同的执行计划?

EDIT 编辑

The query that EF builds is one of the ones where it builds a giant string with the parameter included in the text, not as a SQL parameter: 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]'

EDIT 编辑

I'm not adding this as an answer since it doesn't actually address the underlying issue, but this did end up getting resolved by rebuilding indexes and recomputing statistics. 我不是将此作为答案添加,因为它实际上并没有解决潜在的问题,但最终通过重建索引和重新计算统计数据来解决这个问题。 That hadn't been done in longer than usual, and it seems to have cleared up whatever caused the issue. 没有比平时更长的时间,这似乎已经解决了造成这个问题的任何问题。

I'll keep reading up on some of the links here in case this happens again, but since it's all working now and unreproduceable, I don't know if I'll ever know for sure exactly what it was doing. 我会继续阅读这里的一些链接,以防再次发生这种情况,但由于它现在都在工作并且无法实现,我不知道我是否确实知道它到底在做什么。

Thanks for all the ideas. 感谢所有的想法。

I recently had a very similar scenario, a query would run very fast executing it directly in the database, but had terrible performance using EF (version 5, in my case). 我最近有一个非常类似的场景,一个查询将在数据库中直接执行它非常快,但使用EF(在我的情况下版本5)有可怕的性能。 It was not a network issue, the difference was from 4ms to 10 minutes. 这不是一个网络问题,差异是从4毫秒到10分钟。

The problem ended up being a mapping problem. 问题最终成为映射问题。 I had a column mapped to NVARCHAR , while it was VARCHAR in the database. 我有一个映射到NVARCHAR的列,而它在数据库中是VARCHAR Seems inoffensive, but that resulted in an implicit conversion in the database, which totally ruined the performance. 似乎无害,但这导致数据库中的隐式转换,这完全破坏了性能。

I'm not entirely sure on why this happens, but from the tests I made, this resulted in the database doing an Index Scan instead of an Index Seek , and apparently they are very different performance-wise. 我不完全确定为什么会发生这种情况,但是根据我的测试,这导致数据库执行索引扫描而不是索引搜索 ,显然它们在性能方面非常不同。

比较索引扫描和索引搜索

I blogged about this here (disclaimer: it is in Portuguese), but later I found that Jimmy Bogard described this exact problem in a post from 2012, I suggest you check it out. 我在这里写博客(免责声明:它是葡萄牙语),但后来我发现Jimmy Bogard在2012年的帖子中描述了这个确切的问题,我建议你看一下。

Since you do have a convert in your query, I would say start from there. 既然你的查询中有转换,我会说从那里开始。 Double check all your column mappings and check for differences between your table's column and your entity's property. 仔细检查所有列映射,并检查表的列与实体属性之间的差异。 Avoid having implicit conversions in your query. 避免在查询中进行隐式转换。
If you can, check your execution plan to find any inconsistencies, be aware of the yellow warning triangle that may indicate problems like this one about doing implicit conversion: 如果可以,请检查执行计划以找出任何不一致的地方,请注意黄色警告三角形,它可能表示有关进行隐式转换的问题:

查询问题隐式转换警告

I hope this helps you somehow, it was a really difficult problem for us to find out, but made sense in the end. 我希望这能以某种方式帮助你,这对我们来说是一个非常难以找到的问题,但最终还是有意义的。

There is an excellent article about Entity Framework performance consideration here . 有一个关于实体框架性能考虑一个很好的文章在这里

I would like to draw your attention to the section on Cold vs. Warm Query Execution: 我想提请您注意关于冷与热查询执行的部分:

The very first time any query is made against a given model, the Entity Framework does a lot of work behind the scenes to load and validate the model. 第一次针对给定模型进行任何查询时,实体框架在幕后进行了大量工作以加载和验证模型。 We frequently refer to this first query as a "cold" query. 我们经常将此第一个查询称为“冷”查询。 Further queries against an already loaded model are known as "warm" queries, and are much faster. 针对已经加载的模型的进一步查询被称为“热”查询,并且速度更快。

During LINQ query execution, the step "Metadata loading" has a high impact on performance for Cold query execution. 在LINQ查询执行期间,“元数据加载”步骤对冷查询执行的性能具有很大影响。 However, once loaded metadata will be cached and future queries will run much faster. 但是,一旦加载的元数据将被缓存,未来的查询将运行得更快。 The metadata are cached outside of the DbContext and will be re-usable as long as the application pool lives. 元数据缓存在DbContext之外,只要应用程序池存在,就可以重复使用。

In order to improve performance, consider the following actions: 为了提高性能,请考虑以下操作:

  • use pre-generated views 使用预先生成的视图
  • use query plan caching 使用查询计划缓存
  • use no tracking queries (only if accessing for read-only) 不使用跟踪查询(仅在以只读方式访问时)
  • create a native image of Entity Framework (only relevant if using EF 6 or later) 创建实体框架的本机映像(仅在使用EF 6或更高版本时才相关)

All those points are well documented in the link provided above. 所有这些要点都在上面提供的链接中有详细记录。 In addition, you can find additional information about creating a native image of Entity Framework here . 此外,您还可以在此处找到有关创建实体框架的本机映像的其他信息。

I don't have an specific answer as to WHY this is happening, but it certainly looks to be related with how the query is handled more than the query itself. 我没有具体的答案为什么会发生这种情况,但它肯定与查询的处理方式有关,而不是查询本身。 If you say that you don't have any issues running the same generated query from SSMS, then it isn't the problem. 如果您说从SSMS运行相同的生成查询没有任何问题,那么这不是问题。

A workaround you can try: A stored procedure. 您可以尝试一种解决方法:存储过程。 EF can handle them very well, and it is the ideal way to deal with potentially complicated or expensive queries. EF可以很好地处理它们,它是处理可能复杂或昂贵的查询的理想方式。

Just to put this out there since it has not been addressed as a possibility: 只是把它放在那里,因为它没有被解决的可能性:

Given that you are using Entity Framework (EF), if you are using Lazy Loading of entities, then EF requires Multiple Active Result Sets (MARS) to be enabled via the connection string. 鉴于您正在使用实体框架(EF),如果您使用实体的延迟加载,则EF需要通过连接字符串启用多个活动结果集(MARS)。 While it might seem entirely unrelated, MARS does sometimes produce this exact behavior of something running quickly in SSMS but horribly slow (seconds become several minutes) via EF. 虽然它看起来可能完全不相关,但MARS确实有时会产生SSMS中快速运行的某些行为,但通过EF可以非常缓慢地(几秒钟变成几分钟)。

One way to test this is to turn off Lazy Loading and either remove MultipleActiveResultSets=True; 测试此方法的一种方法是关闭延迟加载并删除MultipleActiveResultSets=True; (the default is "false") or at least change it to be MultipleActiveResultSets=False; (默认为“false”)或至少将其更改为MultipleActiveResultSets=False; .

As far as I know, there is unfortunately no work-around or fix (currently) for this behavior. 据我所知,遗憾的是,这种行为没有解决方法或修复(目前)。

Here is an instance of this issue: Same query with the same query plan takes ~10x longer when executed from ADO.NET vs. SMSS 以下是此问题的一个实例: 从ADO.NET与SMSS执行时,具有相同查询计划的相同查询需要大约10倍的时间

Realizing you are using Entity Framework 4.1, I would suggest you upgrade to Entity Framework 6. 意识到您正在使用Entity Framework 4.1,我建议您升级到Entity Framework 6。

There has been a lot of performance improvement and EF 6 is much faster than EF 4.1. 已经有很多性能提升,EF 6比EF 4.1快得多。

The MSDN article about Entity Framework performance consideration mentioned in my other response has also a comparison between EF 4.1 and EF 6. 关于我在其他响应中提到的实体框架性能考虑MSDN文章也对EF 4.1和EF 6进行了比较。

There might be a bit of refactoring needed as a result, but the improvement in performance should be worth it (and that would reduce the technical debt at the same time). 结果可能需要一些重构,但性能的提高应该是值得的(这将同时减少技术债务)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM