我为新项目评估了几种.NET数据库访问技术,并在使用Entity Framework查询远程数据库时识别出性能不佳的行为。实体框架比LinqToSql或SqlClient慢10倍。也许你可以帮我解释或修复它?
测试参数:
数据库:
表格结构:
[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)
基准申请:
数据库访问技术:
计算机(客户端/服务器):
我直接使用不同计算机上的每种数据库访问技术作为客户端或服务器执行sql查询select * from Master
。平均时间是1000次迭代的结果。
测试场景1:
服务器:A
实体框架:平均时间:17毫秒
测试场景2:
服务器:B
实体框架:平均时间:144毫秒
测试场景3:
服务器:C
实体框架:平均时间:2145毫秒
测试场景4:
服务器:C
实体框架:平均时间:2060毫秒
为什么测试场景3和4中的实体框架比LinqToSQL或SqlClient慢10倍?
我使用Entity Framework 4.3.1,5(RC)和.NET Framework 4和4.5进行了测试,每次都得到相同的结果。我禁用了延迟加载和跟踪,使用了编译查询和预生成视图,但没有区别。
我使用SQL事件探查器调查执行的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.是否有人可以解释或建议如何加快执行速度?
提前致谢
的Mathias
答案 0 :(得分:3)
如果没有看到你的代码,就很难给出任何明智的猜测,但是你可以通过一些典型的东西来看看。
通常,您可以使用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):
提高绩效的策略 你可以改善整体 使用以下内容在Entity Framework中执行查询 策略。
预生成视图
基于实体生成视图 模型是第一次申请时的重大成本 执行查询。使用EdmGen.exe实用程序预生成视图 一个Visual Basic或C#代码文件,可以在此期间添加到项目中 设计。您还可以使用文本模板转换工具包 生成预编译的视图。预生成的视图在以下位置验证 运行时以确保它们与当前版本一致 指定的实体模型。有关更多信息,请参见如何: 预生成视图以提高查询性能(实体框架)和 使用预编译/预生成视图隔离性能 实体框架4.使用非常大的模型时,如下所示 考虑适用:.NET元数据格式限制了数量 给定二进制文件中的用户字符串字符为16,777,215(0xFFFFFF)。如果 您正在为非常大的模型和视图文件生成视图 达到此大小限制,您将获得&#34;没有剩余的逻辑空间 创建更多用户字符串。&#34;编译错误。此大小限制适用 所有托管的二进制文件。有关更多信息,请参阅博客 演示了如何在使用large和。时避免错误 复杂的模型。
考虑对查询使用NoTracking合并选项
跟踪对象中的返回对象需要一定的成本 上下文。检测对象的更改并确保多个 对同一逻辑实体的请求返回相同的对象实例 要求将对象附加到ObjectContext实例。如果你 不打算对对象进行更新或删除,也不要求 身份管理,考虑使用NoTracking合并选项时 你执行查询。
返回正确的数据量
在一些人中 方案,使用Include方法指定查询路径很多 更快,因为它需要更少的往返数据库。然而, 在其他情况下,额外往返数据库进行加载 相关对象可能更快,因为更简单的查询更少 连接导致数据冗余减少。因此,我们建议 你测试各种方法的性能来检索相关的 对象。有关更多信息,请参阅整形查询结果(实体 框架)。为避免在单个查询中返回过多数据, 考虑将查询结果分页到更易于管理的组中。 有关更多信息,请参见如何:通过查询结果页面(实体 框架)。
限制ObjectContext的范围
在大多数情况下,你 应该在using语句中创建一个ObjectContext实例 (使用...在Visual Basic中结束使用)。这可以提高性能 确保与对象上下文关联的资源 当代码退出语句块时自动处理。 但是,当控件绑定到对象管理的对象时 在上下文中,只要维护ObjectContext实例 需要手动绑定和处理。欲获得更多信息, 请参阅管理对象服务中的资源(实体框架)。
考虑手动打开数据库连接
申请时 执行一系列对象查询或经常调用SaveChanges 坚持创建,更新和删除数据源的操作, 实体框架必须不断打开和关闭连接 数据源。在这些情况下,请考虑手动打开 这些操作开始时的连接以及关闭或 操作完成后处理连接。更多 信息,请参阅管理实体中的连接和事务 框架。
答案 1 :(得分:2)
不幸的是,我不知道他们改变了什么,并猜测我永远无法找到答案。非常失望,因为我知道我不知道原因是什么。
谢谢。