【问题标题】:How to reduce Entity Framework 4 query compile time?如何减少 Entity Framework 4 查询编译时间?
【发布时间】:2010-04-29 17:33:43
【问题描述】:

总结:我们遇到了 EF4 查询编译时间超过 12 秒的问题。缓存查询只能让我们走这么远;有什么方法可以真正减少编译时间?有什么我们可以寻找的可能做错的事情吗?谢谢!

我们有一个通过 WCF 服务公开的 EF4 模型。对于我们的每种实体类型,我们公开了一个方法来获取和返回整个实体以供显示/编辑,包括许多引用的子对象。

对于一个特定的实体,我们必须 .Include() 31 个表/子表来返回所有相关数据。不幸的是,这使得 EF 查询编译速度非常慢:编译和构建一个 7,800 行、300K 的查询需要 12-15 秒。这是 Web UI 的后端,需要比这更敏捷。

我们可以做些什么来改善这一点?我们可以 CompiledQuery.Compile 这个 - 在第一次使用之前不会做任何工作,因此有助于第二次和后续执行,但我们的客户担心第一次使用也不应该很慢。同样,如果托管 Web 服务的 IIS 应用程序池被回收,我们将丢失缓存计划,尽管我们可以增加生命周期以最小化这种情况。此外,我看不到提前预编译和/或序列化 EF 编译查询缓存(缺少反射技巧)的方法。 CompiledQuery 对象仅包含对缓存的 GUID 引用,因此它是我们真正关心的缓存。 (写出来我突然想到我可以从 app_startup 在后台启动一些东西来执行所有查询以编译它们 - 这安全吗?)

但是,即使我们确实解决了这个问题,我们也会根据我们正在搜索的参数使用 LINQ-to-Entities 子句动态构建搜索查询:我认为 SQL 生成器做得不够好我们可以将所有这些逻辑移动到 SQL 层,所以我认为我们不能预编译我们的搜索查询。这不太严重,因为搜索数据结果使用较少的表,因此编译时间仅为 3-4 秒而不是 12-15 秒,但客户认为最终用户仍然无法接受。

所以我们确实需要以某种方式减少查询编译时间。有什么想法吗?

  • Profiling 指向 ELinqQueryState.GetExecutionPlan 作为开始的地方,我试图进入它,但没有可用的真正 .NET 4 源,我无法走得很远,Reflector 生成的源不会让我进入某些函数或在其中设置断点。
  • 项目是从 .NET 3.5 升级的,所以我尝试在 EF4 中从头开始重新生成 EDMX,以防万一出现问题,但这并没有帮助。
  • 我已经尝试过此处宣传的 EFProf 实用程序,但它看起来对此没有帮助。无论如何,我的大型查询会使它的数据收集器崩溃。
  • 我已经通过 SQL 性能调优运行了生成的查询,它已经有 100% 的索引使用率。我看不出数据库有什么问题会导致查询生成器出现问题。
  • 执行计划编译器中是否存在 O(n^2) 的问题 - 是否将其分解为单独的数据加载块,而不是同时处理所有 32 个表?将 EF 设置为延迟加载没有帮助。
  • 我已经购买了预发行版的 O'Reilly Julie Lerman EF4 书籍,但除了“编译您的查询”之外,我无法在其中找到任何帮助。

我不明白为什么在 32 个表中生成单个选择需要 12-15 秒,所以我很乐观,还有一些改进的余地!

感谢您的任何建议!我们在 SQL Server 2008 上运行以防万一,而 XP / 7 / server 2008 R2 使用 RTM VS2010。

【问题讨论】:

  • 我们也有这个问题,这是一个大问题。执行查询的时间最多为 2-300 毫秒,而等待 ELinqQueryState.GetExecutionPlan() 需要 8-9 秒。那是在将 EF 无法处理的大型急切负载查询(过去每个 qry 需要 2-4 分钟等待 ELinqQueryState.GetExecutionPlan())拆分为许多更简单的急切负载查询之后。我想我们能做的就是等待 EF5,它应该可以减轻由此造成的一些痛苦。
  • 我已经在 msdn.forums 上发布了关于这个问题的帖子,希望能得到一些指导,在这里social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/…
  • @Mahol25 谢谢 - 我会看那个帖子。很抱歉,在我离开项目之前,我们从来没有把我们的查询降到不到一秒钟,所以除了已经在这里的内容之外,我没有任何具体的建议可以给你。

标签: .net entity-framework entity-framework-4


【解决方案1】:

让您的查询更简单。严重地;查询复杂度和编译时间之间几乎呈线性关系。两个简单的查询通常比一个真正复杂的查询快得多(即使是预编译的!)。如果速度是最终目标,请选择最快的选项。

【讨论】:

  • 谢谢,很抱歉花了很长时间才回来。是的,这就是我们最终或多或少地为显示读取案例所做的事情:将对象加载分成几个较小的块,并为每个块预先编译查询,然后将检索到的数据重新组合到我们使用的 POCO 对象中数据传输。为了安全起见,我们坚持对 read-for-update 案例进行单个大型查询,而不是尝试将 EF 数据缝合在一起以获得组合结果对象。
  • 吹毛求疵:线性关系意味着将其拆分不会带来任何好处,并且可能会由于更高的每次查询常数而损害它。
  • @MerlynMorgan-Graham,我们的目标是选择一对(或更多)更简单但等效的查询,而不是将复杂性分成两半。
  • @CraigStuntz:那么这可能是有道理的。
  • @Mahol25 您的失败并不意味着建议不正确。你可以责怪你的工具或责怪我,但你不能仅仅因为你得到 8 秒以上的查询就断定我有一个“简单的数据模型”。这简直是​​愚蠢的。
【解决方案2】:

您可以为一些更复杂的查询创建一个视图,从而完全控制 SQL。然后将该视图包含在您的数据模型中。

【讨论】:

  • 谢谢,很抱歉花了很长时间才回来。是的,我们最终为一些只读查询(搜索结果等)创建了视图,但我们坚持为一般对象获取构建对象树。
【解决方案3】:

这可能不是您要寻找的答案,但是对于一个简单的解决方法,为什么不在 webapp 初始化时运行 CompiledQuery.Compile(对 webapp 进行一些虚拟调用)而不是第一个 (客户)打电话。

【讨论】:

  • 谢谢。是的,如果我可以在后台执行此操作,那值得一试 - 我不想在每次应用程序池重新启动时编译所有查询的几分钟内锁定站点。但是,它无助于加快我们即时构建的搜索查询,因此据我所知无法缓存。 (CompiledQuery.Compile 调用实际上只是将查询与缓存 GUID 相关联并立即返回 - 直到第一次执行才真正发生编译。所以我必须在启动时对每个已编译查询进行虚拟提取以触发编译。)
  • 我们在应用程序初始化时将其实现为后台线程,但不幸的是,它似乎并不总是有效;它在单线程情况下工作正常,并且 EF4 缓存代码中没有线程依赖性,所以我对为什么它并不总是有效有点茫然。然而,这在某些情况下确实有帮助,所以我们坚持使用它。感谢您的建议!
【解决方案4】:

您可以尝试使用http://www.iis.net/download/ApplicationWarmup 它现在处于测试阶段,但我们也打算使用它。

【讨论】:

  • 感谢您的建议,很抱歉花了很长时间才回到这里。权力说不,至少在它仍然是测试版的时候没有,所以我们最终在后台线程中做了类似的事情——不幸的是,成功有限:-/
【解决方案5】:

看看nqueries,它使用了在应用程序启动期间生成的预编译查询。

编辑: 自从切换到 EF5 后,NQueries 不再支持编译查询,因为它切换到了 DbContext,如果您还想看,请看1.03 release source code,它仍然是基于 ObjectContext 并支持编译查询。

【讨论】:

  • 谢谢。我会看看,但我们已经在生成 CompiledQueries 并尽可能使用预编译视图 - 我看不出它在启动时还能做什么。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-06-05
  • 2014-06-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多