实体框架 - 第一次查询缓慢

时间:2010-10-08 13:42:47

标签: .net performance entity-framework

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

我曾尝试在不同的网站上寻找答案,但似乎没有人真正找到解决方案。

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

使用Entity Framework 4.0 Model和POCO生成器在Visual Studio 2010中完成测试(普通实体和POCO对象之间的时间差别不大)。我还使用T4视图模板预编译视图。

数据库位于SQL Server 2008上。

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

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

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

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

时间安排如下:

.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。我为文本墙道歉:)。

编辑19-10-2010:
我昨天做了一个测试,似乎支持以顺序方式返回行。这意味着当从数据库返回一行时,它会立即实现(如果它在上下文中尚不存在),则返回下一行,依此类推。

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

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

离。

  1. 使用EF运行第一个查询,SQL语句比任何辅助查询慢
  2. 处理上下文/存储库
  3. 创建新上下文
  4. 运行与以前相同的查询(同样第一个查询很慢,SQL语句也是如此)
  5. 这几乎就像EF发送了一些选项以及使服务器变慢的第一个查询。

    至于查询编译,我记得在第一次使用时编译了查询,这意味着第一个查询执行的时间会更长。

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

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

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

    差别不是那么大,只有几秒钟,而且我第一次运行查询时,它仍然只需要预先编译就可以了。

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

    如果除了最简单的查询只返回相对少量的数据之外什么都不做,那么EF的答案是否真的有用吗?

4 个答案:

答案 0 :(得分:13)

我们在EF 5.0中遇到了同样的问题,到目前为止,一个肤浅的谷歌搜索并没有显示出足够的速度。

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

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

请注意,在我们的上下文中,用户可能需要在应用程序中花费18-19秒才能进行EF调用以响应其操作。显然,如果在您的应用程序中无法做到这一点,那么这项工作可能无法提高速度。

答案 1 :(得分:4)

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

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

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

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

答案 2 :(得分:1)

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

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

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

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

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

答案 3 :(得分:0)

我们有同样的问题。这只是Code的第一种方法。我们有大约1500个POCO(+1500个POCO映射文件)。只需编译大约需要1-2分钟。 Context.table.Add()方法大约需要3-4分钟,但对于第一个对象。这就像一个糟糕的笑话,没有任何解决方案。 这3-4分钟可能是某种EF“POCO转型”。一个CPU核心运行100%,SQL分析器中没有任何功能。

使用数据库第一种方法(生成edmx xml文件)为相同的1500个表正常工作。它的速度和预期一样快。

到目前为止无处可寻。也许EF6会解决这个问题。