2

我为我们的新项目评估了几种 .NET 数据库访问技术,并发现使用 Entity Framework 查询远程数据库时性能不佳。Entity Framework 比 LinqToSql 或 SqlClient 慢 10 倍。也许你可以帮我解释或解决这个问题?

测试参数:

数据库:

  • SQL Server 2008 企业版
  • 一张有 1000 条记录的表
  • 表结构:

    [dbo].[Master](
           [Id] [int] IDENTITY(1,1) NOT NULL,
           [Value_Bit] [bit] NOT NULL,
           [Value_Float] [float] NOT NULL,
           [Value_DateTime] [datetime2](7) NOT NULL,
           [Value_Uniqueidentifier] [uniqueidentifier] NOT NULL,
           [Value_NVarchar100] [nvarchar](100) NOT NULL,
           [Value_NVarchar1000] [nvarchar](1000) NOT NULL,
           [InsertDate] [datetime] NOT NULL,
           [UpdateDate] [datetime] NOT NULL,
           [Version] [timestamp] NOT NULL)
    

基准应用:

  • .NET 框架 4 和 4.5
  • 作为 WinForms 应用程序托管

数据库访问技术:

  • 实体框架 5.0 (RC) 和 4.3.1
  • LinqToSQL
  • SqlClient

计算机(客户端/服务器):

  • 三台具有相似硬件的不同计算机
  • A 和 B 在同一个子网中(例如 192.168.1.1 和 192.168.1.2)
  • C 与 A 和 B 在不同的子网中(例如 192.168.2.1)

select * from Master我在作为客户端或服务器的不同计算机上直接使用每种数据库访问技术执行 sql 查询。平均时间是 1000 次迭代的结果。

测试场景一:

  • 客户:A
  • 服务器:A

  • 实体框架:平均时间:17 毫秒

  • LinqToSQL:平均时间:20 毫秒
  • SqlClient:平均时间:15 毫秒

测试场景二:

  • 客户:A
  • 服务器:B

  • 实体框架:平均时间:144 毫秒

  • LinqToSQL:平均时间:141 毫秒
  • SqlClient:平均时间:140 毫秒

测试场景 3:

  • 客户:A
  • 服务器:C

  • 实体框架:平均时间:2145 毫秒

  • LinqToSQL:平均时间:151 毫秒
  • SqlClient:平均时间:156 毫秒

测试场景4:

  • 客户:乙
  • 服务器:C

  • 实体框架:平均时间:2060 毫秒

  • LinqToSQL:平均时间:141 毫秒
  • SqlClient:平均时间:178 毫秒

为什么测试场景 3 和 4 中的实体框架比 LinqToSQL 或 SqlClient 慢 10 倍?

我在 Entity Framework 4.3.1、5 (RC) 和 .NET Framework 4 和 4.5 下对其进行了测试,每次都得到相同的结果。我禁用了延迟加载和跟踪,使用编译查询和预生成视图,但没有区别。

我使用 SQL Profiler 调查执行的 SQL 查询,发现实体框架的查询在 SQL Server 中已经花费了两秒钟(测试场景 3)。如果我从计算机 A 上的 Management Studio 执行查询,只需要 100 毫秒。

我使用 dotTrace (http://www.jetbrains.com) 分析了我的基准应用程序,发现大部分执行时间都被方法消耗掉了ToList。如果我深入调用堆栈,我会看到该方法System.Data.SqlClient.SqlDataReader.GetString(Int32),最后SNINativeMethodWrapper.SNIReadSyncOverAsync(SafeHandle, IntPtr&, Int32)会一直消耗。LinqToSql 也使用了 SqlClient,调用栈几乎一样,但是执行时间快了 10 倍。

我不知道引擎盖下发生了什么。也许它与计算机名称解析有关,但我可以通过 IP 地址及其计算机名称 ping 计算机 C。有没有人可以解释或建议如何加快执行速度?

提前致谢

马蒂亚斯

4

2 回答 2

3

如果没有看到您的代码,很难给出任何明智的猜测,但是您可以大致了解 EF 的一些典型内容。

一个常见的错误是执行返回 IEnumerable 或 IQueryable 集合的查询,然后在循环中使用它:

// Execution will be deferred:
IEnumerable<person> peopleList = objectContext.People.Where(item => item.ID > 100);
foreach (person somePerson in peopleList)
{
  // do something here
}

此代码将对数据库执行很多次往返,这可能会导致严重的性能问题。由于延迟执行和延迟加载,这将导致人员列表中每个项目的代码再次查询数据库。根据通过网络传输的数据量,仅此一项就可能严重损害性能。

您可以通过简单地在集合上调用 ToList() 方法来减少这种开销。这仅在一次往返中获取所有结果对象:

// Execution will be deferred:
List<person> peopleList = objectContext.People.Where(item => item.ID > 100)
                                              .ToList(); // Fetch objects NOW!

MSDN 提供了一篇文章,其中包含一些建议,Performance Considerations (Entity Framework)

提高性能的策略 您可以通过使用以下策略来提高实体框架中查询的整体性能。

预生成视图

在应用程序第一次执行查询时,基于实体模型生成视图是一笔巨大的开销。使用 EdmGen.exe 实用程序将视图预生成为可在设计期间添加到项目的 Visual Basic 或 C# 代码文件。您还可以使用文本模板转换工具包生成预编译视图。预生成的视图在运行时进行验证,以确保它们与指定实体模型的当前版本一致。有关更多信息,请参阅如何:预生成视图以提高查询性能(实体框架)和在实体框架中使用预编译/预生成的视图隔离性能 4. 使用非常大的模型时,适用以下注意事项:. NET 元数据格式将给定二进制文件中的用户字符串字符数限制为 16 个,777,215 (0xFFFFFF)。如果您正在为一个非常大的模型生成视图并且视图文件达到此大小限制,您将得到“没有剩余逻辑空间来创建更多用户字符串”。编译错误。此大小限制适用于所有托管二进制文件。有关更多信息,请参阅演示如何在使用大型复杂模型时避免错误的博客。

考虑对查询使用 NoTracking 合并选项

在对象上下文中跟踪返回的对象需要成本。检测对象的更改并确保对同一逻辑实体的多个请求返回同一对象实例需要将对象附加到 ObjectContext 实例。如果您不打算对对象进行更新或删除并且不需要身份管理,请考虑在执行查询时使用 NoTracking 合并选项。

返回正确的数据量

在某些情况下,使用 Include 方法指定查询路径要快得多,因为它需要更少的数据库往返。但是,在其他情况下,额外往返数据库以加载相关对象可能会更快,因为具有较少连接的更简单查询会导致数据冗余较少。因此,我们建议您测试各种检索相关对象的方法的性能。有关更多信息,请参阅塑造查询结果(实体框架)。为避免在单个查询中返回过多数据,请考虑将查询结果分页到更易于管理的组中。有关更多信息,请参阅如何:浏览查询结果(实体框架)。

限制 ObjectContext 的范围

在大多数情况下,您应该在 using 语句中创建一个 ObjectContext 实例(Visual Basic 中的 Using...End Using)。这可以通过确保在代码退出语句块时自动释放与对象上下文关联的资源来提高性能。但是,当控件绑定到由对象上下文管理的对象时,只要需要绑定并手动处理,就应该维护 ObjectContext 实例。有关详细信息,请参阅管理对象服务中的资源(实体框架)。

考虑手动打开数据库连接

当您的应用程序执行一系列对象查询或频繁调用 SaveChanges 以持续对数据源进行创建、更新和删除操作时,实体框架必须不断打开和关闭与数据源的连接。在这些情况下,请考虑在这些操作开始时手动打开连接,并在操作完成时关闭或释放连接。有关更多信息,请参阅在实体框架中管理连接和事务。

于 2012-09-11T12:09:56.570 回答
2

问题分别巨大的时间间隔突然消失了。有人告诉我管理员更改了网络上的某些内容,因此我推断这与此有关,因为我没有更改测试参数的任何内容。

不幸的是,我不知道他们发生了什么变化,我猜我永远也无法找到答案。非常令人失望,因为我知道我不知道原因是什么。

谢谢你。

于 2012-09-11T11:44:00.890 回答