【问题标题】:Entity Framework - Bad performance when querying remote databases实体框架 - 查询远程数据库时性能不佳
【发布时间】:2014-05-08 15:20:15
【问题描述】:

我为我们的新项目评估了几种 .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 Framework 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)

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

测试场景一:

  • 客户:A
  • 服务器:A

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

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

测试场景 2:

  • 客户:A
  • 服务器:B

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

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

测试场景 3:

  • 客户:A
  • 服务器:C

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

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

测试场景 4:

  • 客户:B
  • 服务器: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。有没有人可以解释一下或有建议如何使执行更快?

提前致谢

马蒂亚斯

【问题讨论】:

    标签: database performance entity-framework


    【解决方案1】:

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

    • 您通常可以使用Compiled Queries 显着加快处理速度。

    • 在查看代码时,延迟执行 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 中使用...结束使用)。这可以通过以下方式提高性能 确保与对象上下文关联的资源是 当代码退出语句块时自动释放。 但是,当控件绑定到对象管理的对象时 上下文,只要保持 ObjectContext 实例 需要绑定并手动处理。了解更多信息, 请参阅在对象服务中管理资源(实体框架)。

    考虑手动打开数据库连接

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

    【讨论】:

    • 哎呀,好像你在我写我的时候已经发布了你自己的答案。没关系......我仍然希望这至少对一般情况有一点帮助......
    【解决方案2】:

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

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

    谢谢。

    【讨论】:

    • 欢迎来到美国公司。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-04-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多