【问题标题】:Entity Framework - First query slow实体框架 - 第一次查询慢
【发布时间】:2011-04-22 21:52:25
【问题描述】:

正如标题所示,我在使用 Entity Framework 对 SQL Server 数据库进行第一次查询时遇到问题。

我曾尝试在不同的网站上寻找答案,但似乎没有人能真正解决这个问题。

我正在从数据库中加载很多行,包括两个 0-many 关系。

测试是在 Visual Studio 2010 中使用 Entity Framework 4.0 模型和 POCO 生成器完成的(普通实体和 POCO 对象之间的计时没有太大差异)。我还使用了 T4 视图模板来预编译视图。

数据库位于 SQL Server 2008 上。

我真正想知道的是为什么第一个查询比任何第二个查询都要慢。

我还想知道是否可以采取措施将第一个查询的速度提高到可以接受的范围内。

这是一个大查询,我们可能会得到其他更大的查询,可以理解它们可能有点慢,但是 30 秒对于用户来说太慢了,尤其是当数据集可以获得相同的数据时快很多。

我已经做了一些时间测试,试图找出问题所在,我有点惊讶地发现它看起来是 SQL Server 在第一次查询时很慢。

时间安排如下:

.NET 测试应用程序:

  • 第一次查询:29.6 秒
  • 第二次查询:3.2 秒

SQL 探查器:

  • 第一个查询:27 秒
  • 第二次查询:3.2 秒

SQL Server 查询窗口

  • 第一次查询:8 秒
  • 第二次查询:4 秒

应用程序中的时间是用Stopwatch 类测量的。仅测量查询并使用.ToList() 执行查询。

SQL Server Profiler 中的计时用于在应用程序中执行的相同查询,这表明应用程序仅使用大约 2.6 秒将数据填充到对象中。

最后 27 秒用于在 SQL Server 上执行查询。

查看辅助查询,应用程序和 SQL 服务器的时间相同,但这次执行查询要快得多。

我可以理解为什么应用程序不使用任何时间,因为没有需要转换为对象的新行,但是为什么查询要快得多,由于执行计划,我预计会花费几秒钟,但不是24 秒。

出于测试目的,我复制了实体框架生成的 SQL,并打开了一个带有单独连接的新查询窗口并在其中执行了查询。

如您所见,第一个查询需要 8 秒,第二个查询需要 4 秒。

希望有人给点建议。

ps。我为文字墙道歉:)

2010 年 19 月 10 日编辑:
我昨天做了一个测试,似乎支持按顺序返回行。这意味着当从数据库返回一行时,它会立即具体化(如果它在上下文中不存在),然后返回下一行,依此类推。

这就是查询在数据库服务器上花费大量时间的原因,因为实现时间包含在 SQL Server 分析器计时中。

我不认为这是 SQL Server 从硬盘读取的情况。 每次在 EF 中存在“第一个查询”时,都会发生慢查询。

例如

  1. 使用 EF 运行第一个查询,SQL 语句比任何辅助查询都慢
  2. 处置上下文/存储库
  3. 创建新上下文
  4. 运行与之前相同的查询(同样,第一个查询很慢,SQL 语句也很慢)

这几乎就像 EF 在第一个查询中发送一些选项一样使服务器变慢。

关于查询编译,我记得查询是在第一次使用时编译的,这意味着第一个查询需要更长的时间才能执行。

辅助查询会更快,但辅助查询的速度不是问题。

我还做了一个测试,我创建了一个静态的编译查询,以便为所有创建的上下文编译它。

然后我创建了一个上下文,运行了查询,销毁了上下文并创建了一个新的上下文并再次运行了相同的查询。

差异并没有那么大,只有几秒钟,而且我第一次运行查询时,只要不预编译它,它仍然需要很长时间。

至于视图生成,我们已经使用 T4 模板实现了这一点。

答案真的是 EF 只有在您只返回相对少量数据的最简单查询之外什么都不做的情况下才有效?

【问题讨论】:

  • 我刚刚在一个旧项目中遇到了这个问题,即使这已经 9 年了,也没有真正带来解决方案的答案,是否有任何更新的 EF 版本解决了这个问题?

标签: .net performance entity-framework


【解决方案1】:

我们在 EF 5.0 中遇到了同样的问题,截至今天,肤浅的 Google 搜索并没有显示出足够的加速。

根据此链接http://msdn.microsoft.com/en-us/library/cc853327(v=vs.100).aspx“加载元数据”的时间成本适中,但每个 AppDomain 只需要发生一次。我发现没有像加载元数据这样的预编译技巧。

我们实施的解决方法是在应用程序启动时在单独的线程中对上下文进行次要查询。这会加载元数据,它仍然需要很长时间(在我们的例子中为 18-19 秒),但应用程序在加载期间是响应式的。此外,第一次实际加载也不会花很长时间。

请注意,在我们的上下文中,用户可能会在应用程序中花费 18-19 秒,然后才需要进行 EF 调用以响应他们的操作。显然,如果这在您的应用程序中是不可能的,那么这种解决方法可能不会提供太多的速度提升。

【讨论】:

    【解决方案2】:

    我有同样的问题。我使用了一个技巧来解决这个问题。由于实体框架在第一次访问时需要更多时间,然后它第一次在它的级别上缓存一些结果(sql server 也单独缓存结果)。所以我访问了我的应用程序异步启动的实体框架。它对我有用。我的申请也变得更流畅了。

    在 Global.asax 页面中

     protected void Application_Start()
        {
    
            Start(() =>
            {
                using (EF.DMEntities context = new EF.DMEntities())
                {
                    context.DMUsers.FirstOrDefault();
                }
            });
        }
        private void Start(Action a)
        {
            a.BeginInvoke(null, null);
        } 
    

    【讨论】:

    • 经过数小时的研究,这确实加速了我的“冷”查询。
    • 在第一次访问(1-2 秒)时 EF Core 速度变慢,我通过在加载我的第一页的视图模型时发出一个在异步方法中没有返回结果的查询来解决这个问题( Xamarin.Forms 中的主详细信息)。与此解决方案类似。
    【解决方案3】:

    嗯,很多 事情可以使 SQL Server 查询在第一次运行时变慢。然而,它们中的大多数不需要几秒钟。

    ... 硬盘随机访问除外。第一次运行查询时,SQL Server 可能必须从硬盘存储中读取数据库页面。下次运行查询时,这些页面可能在内存中。

    关于实体框架,第一次运行查询时,必须将其编译成 SQL。您可以使用CompiledQuery 类型来预编译实体框架查询,以便在最终用户必须等待之前提前完成这项工作。

    在非常大的模型上,视图生成也需要一些时间。相反,您可以将其移至编译时间。有关更多此类提示,请参阅this article。

    【讨论】:

    • 抱歉,回答迟了。我不认为这是 SQL 服务器从硬盘读取的情况。每次在 EF 中存在“第一个查询”时,都会发生慢查询。例如。 1) 使用 EF 运行第一个查询,SQL 语句比任何辅助查询都慢。 2)处置上下文/存储库。 3)创建一个新的上下文。 4) 运行与以前相同的查询。 (同样,第一个查询很慢,SQL 语句也很慢)这几乎就像 EF 在第一个查询中发送了一些选项,使服务器变慢。
    • 至于查询编译,我相信查询是在第一次使用时编译的,即第一个查询需要更长的时间来执行。辅助查询会更快,但辅助查询的速度不是问题。我还做了一个测试,我创建了一个静态编译查询,以便为所有创建的上下文编译它。然后我创建了一个上下文,运行了查询,处理了上下文,创建了一个新的上下文并再次运行了查询。差别并没有那么大,只有几秒钟,而且我第一次运行查询时,只要不预编译它,它仍然需要很长时间。
    • 至于视图生成,我们已经使用 T4 模板实现了这一点。答案真的是 EF 只有在您只返回相对少量数据的最简单查询之外什么都不做的情况下才有效?
    • 视图预生成是使用 EdmGen 完成的,而不是 T4。听起来你在做别的事情。关于你的第二个结论,在这一点上责怪你的工具似乎有点仓促。您的绩效指标不典型。
    • T4 模板实际上可以用作视图生成,看看下面的链接。 blogs.msdn.com/b/adonet/archive/2008/06/20/…
    【解决方案4】:

    我在搜索以改善 EF 启动时间时偶然发现了这篇文章。由于它没有真正的答案,我将添加我的发现,以便其他人在偶然发现这篇文章时也可以利用它。

    请注意,我使用的是 EF 6,该解决方案仅适用于 EF 6。

    David Roth 发布了一个 article 来解决这个问题。

    Mikael Eliasson 在他的answer 中很好地总结了一个类似的问题:

    1. 使用缓存的数据库模型存储
    2. 生成预编译视图
    3. 使用 n-gen 生成实体框架的预编译版本以避免抖动

    【讨论】:

      【解决方案5】:

      我们有同样的问题。它仅适用于代码优先方法。我们有大约 1500 个 POCO(+1500 个 POCO 映射文件)。只需编译大约需要 1-2 分钟。 Context.table.Add() 方法大约需要 3-4 分钟,但对于第一个对象。这就像一个糟糕的笑话,在任何地方都没有解决方案。 那 3-4 分钟可能是某种 EF “POCO 转换”。一个 CPU 内核 100% 运行,并且 SQL 分析器中没有任何事情发生。

      对相同的 1500 个表使用数据库优先方法(生成 edmx xml 文件)正常工作。正如预期的那样快。

      到目前为止,任何地方都没有解决方案。也许 EF6 会解决这个问题。

      【讨论】:

      • 这和我遇到的情况一模一样。但是,这并不是真正的答案。通过评论传达这一点会更合适。
      • 在 EF6 中我有同样的情况:数据库优先的方法很快,Poco 很慢
      猜你喜欢
      • 2020-12-03
      • 1970-01-01
      • 1970-01-01
      • 2014-12-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-05
      相关资源
      最近更新 更多