实体框架 - 首次查询缓慢

30
正如标题所示,我在使用Entity Framework连接SQL Server数据库的第一个查询中遇到了问题。
我已经尝试在不同的网站上寻找答案,但似乎没有人真正有解决方案。
我从数据库中加载了相当多的行,包括两个0对多关系。
测试是在Visual Studio 2010中使用Entity Framework 4.0 Model和POCO生成器进行的(常规实体和POCO对象之间的时间差异不大)。我还使用了T4 Views模板来预编译视图。
数据库位于SQL Server 2008上。
我真正想知道的是为什么第一个查询比任何次要查询都要慢这么多。
我还想知道是否可以采取某些措施提高第一个查询的速度,使其达到可接受的限制。
这是一个很大的查询,我们可能会得到其他更大的查询,而且它们可能会有点慢,但用户等待30秒的时间太长了,尤其是当数据集可以更快地获取相同的数据时。
我做了一些计时测试,试图找出问题所在。我有点惊讶地发现,第一个查询看起来是SQL Server很慢。
计时如下:
.NET测试应用程序:
- 第一次查询:29.6秒 - 第二次查询:3.2秒
SQL Profiler:
- 第一次查询:27秒 - 第二次查询:3.2秒
SQL Server查询窗口:
- 第一次查询:8秒 - 第二次查询:4秒
应用程序中的计时是使用Stopwatch类测量的。只测量了查询,并使用.ToList()执行了查询。

SQL Server Profiler中的时间轴是针对应用程序执行的相同查询,显示应用程序仅使用了约2.6秒将数据填充到对象中。

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

查看第二个查询,两个应用程序和SQL Server的时间轴都相同,但这次执行查询要快得多。

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

仅用于测试目的,我复制了Entity Framework生成的SQL,并使用单独的连接打开了一个新的查询窗口,并在其中执行了查询。

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

我希望有人能提出一些建议。

附:对于文本墙我深表歉意 :)

编辑19-10-2010:
昨天我进行了一项测试,似乎支持按顺序返回行。这意味着当从数据库返回一行时,它会立即材料化(如果在上下文中不存在),然后返回下一行等等。

这就是为什么数据库服务器上的查询似乎需要很长时间,因为SQL Server Profiler时间轴中包含了材料化时间。

我不认为这是从硬盘上读取数据的情况。 EF中的第一个查询每次都会变慢。

例如:

  1. 使用EF运行第一个查询,该SQL语句比任何辅助查询都慢
  2. 释放上下文/存储库
  3. 创建新的上下文
  4. 再次运行与以前相同的查询(此时第一个查询仍然很慢,SQL语句也是如此)

这就像EF发送了一些选项,使服务器变慢。

就查询编译而言,据我所知,查询在第一次使用时被编译,这意味着第一次查询执行的时间会更长。

二次查询会更快,但是二次查询的速度不是问题所在。

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

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

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

就视图生成而言,我们已经使用T4模板实现了它。

答案是否真的是EF只适用于简单查询并返回相对较少量数据的情况吗?


我刚在一个旧项目中遇到了这个问题,没有任何答案真正提供解决方案,即使这已经有9年了,是否有更新的EF版本修复了这个问题或其他什么? - Eduardo Wada
5个回答

13

我们在EF 5.0中遇到了同样的问题,今天进行了肤浅的谷歌搜索,但没有找到足够的加速方法。

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

我们实现的解决方法是在应用程序启动时在单独的线程中对Context进行轻微的查询。这会加载元数据,尽管仍然需要很长时间(在我们的情况下为18-19秒),但应用程序在加载期间是响应的。而且,第一次实际加载不会花费太长时间。

请注意,在我们的上下文中,用户可能在应用程序中花费18-19秒钟,然后才需要根据他们的操作进行EF调用。显然,如果您的应用程序不可能达到这个目标,那么此工作可能无法提供太多速度增长。


8

我曾经遇到过同样的问题,使用了一个技巧来解决。由于Entity Framework第一次访问需要更多的时间,并且它会在第一次访问时将一些结果缓存到它自己的级别上(SQL Server也会分开缓存结果)。因此我在我的应用程序启动时异步地访问了Entity Framework。这对我有效,我的应用程序变得更加流畅。

在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);
    } 

经过多个小时的研究,这确实加速了我的“冷”查询。 - Darren Alfonso
在第一次访问时,EF Core 速度变慢(1-2 秒),我通过在异步方法中发出返回无结果的查询来解决了这个问题,当加载我的第一个页面的视图模型(Xamarin.Forms 中的主细节)时。类似于此解决方案。 - Sean Anderson

4

很多事情会使得第一次运行SQL Server查询变慢,但大多数情况下不会超过几秒钟。

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

关于Entity Framework,第一次运行查询必须编译成SQL。您可以使用CompiledQuery类型来预编译Entity Framework查询,以便在最终用户等待之前提前完成此工作。

对于非常大的模型,视图生成也需要一些时间。您可以将其移到编译时进行。有关更多提示,请参见此文章


抱歉回复晚了。我不认为这是SQL服务器从硬盘读取的情况。每次在EF中有“第一个查询”时,都会出现慢查询。例如:1)使用EF运行第一个查询,SQL语句比任何次要查询都慢。2)处理上下文/存储库。3)创建新的上下文。4)运行与之前相同的查询。(再次,第一个查询很慢,SQL语句也是如此)。这就像EF发送了一些选项与第一个查询一起使服务器变慢。 - Kenneth Lauritsen
关于查询编译,我认为查询在第一次使用时被编译,即第一个查询执行时间更长。次要查询会更快,但次要查询的速度不是问题所在。我还进行了一个测试,创建了一个静态编译查询,以便为创建的所有上下文编译查询。然后创建了一个上下文,运行了查询,释放了上下文,创建了一个新的上下文并再次运行了查询。差别不是很大,只有几秒钟,而且第一次运行查询时,它仍然像没有预编译一样需要很长时间。 - Kenneth Lauritsen
关于视图生成,我们已经使用T4模板实现了这一点。答案真的是EF只能在您执行的查询非常简单且返回的数据相对较少时才能正常工作吗? - Kenneth Lauritsen
关于查询编译,您有几个错误。1)第一次执行并不需要更长的时间。2)预期应该需要*与第一次同样长的时间,因为查询编译是懒加载;它直到第一次使用才会发生。对.Compile的调用本身基本上是瞬间完成的。我建议您多了解一些相关知识。此外:您可能存在网络问题,导致SQL连接缓慢。在进行分析之前很难确定。 - Craig Stuntz
1
T4模板实际上可以用作视图生成,请查看以下链接。http://blogs.msdn.com/b/adonet/archive/2008/06/20/how-to-use-a-t4-template-for-view-generation.aspx - Kenneth Lauritsen
显示剩余2条评论

1
我在寻找提高EF启动时间的方法时偶然发现了这篇文章。鉴于它并没有真正的答案,我会添加我的发现,以便其他人也可以从中受益。请注意,我正在使用EF 6,并且该解决方案仅适用于EF 6。David Roth发布了一篇文章来解决此问题。Mikael Eliasson在类似问题的答案中很好地总结了它:
  1. 使用缓存的数据库模型存储
  2. 生成预编译视图
  3. 使用n-gen生成预编译版本的entityframework以避免jitting

0
我们遇到了同样的问题,只有在使用Code First方法时才会出现。我们大约有1500个POCO(+1500个POCO映射文件)。仅编译需要大约1-2分钟。Context.table.Add()方法需要大约3-4分钟,但仅适用于第一个对象。这就像一个没有解决方案的恶作剧。
这3-4分钟可能是某种EF“POCO转换”。一个CPU核心运行100%,SQL分析器中没有任何活动。
对于相同的1500个表,使用Database First方法(生成edmx xml文件)可以正常工作。速度如预期一样快。
到目前为止还没有任何解决方案。也许EF6会解决这个问题。

2
这正是我遇到过的完全相同的情况。但这并不真正算是一个答案。更恰当的做法是通过评论来进行交流。 - quakkels
在EF6中,我有同样的情况:数据库优先方法很快,Poco很慢。 - Michał Zych

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接