实体框架6 vs dapper vs存储过程与SQL Server中的请求



我在项目中与SQL Server具有Linq,很长时间。

我正在寻找一种方法来提高我对DB的要求的表现。而且我读了很多关于dapper和程序的信息,它比EF快。我在项目中添加了Dapper,我添加了过程。...但是我的测试显示出奇怪的结果。EF和Dapper和存储程序几乎具有相同的结果 - 没有好处。

首先,我检查了一个有很多加入的请求。我在Dapper和Passingure和EF之间得到了几乎相同的结果。然后,我决定使用一张简单的表进行测试而没有关系。

我有表zipcodes。有43200个记录。

Zipcode表

我使用EF,Dapper,存储过程和SQL Server的请求。

dapper

string query =
            "SELECT TOP (43200) [Zip]rn      ,[City]rn      ,[State]rn      ,[Latitude]rn      ,[Longitude]rn      ,[TimeZone]rn      ,[DST]rn  FROM [dbo].[ZipCodes]";
using (IDbConnection connection = new SqlConnection(_connectionString))
{
    var result = connection.QueryAsync<ZipCodes>(query).Result.ToList();
    return result;
}

ef

var zip = db.ZipCodes.AsNoTracking().Take(43200).ToList();

存储过程

ALTER PROCEDURE [dbo].[ZIPTest]
AS 
BEGIN 
    SELECT TOP (43200) 
        [Zip], [City], [State], [Latitude], [Longitude], [TimeZone], [DST] 
    FROM 
        [dbo].[ZipCodes] 
END

在SQL Server中请求

SELECT GETDATE(); 
SELECT TOP (43200) 
    [Zip], [City], [State], [Latitude], [Longitude], [TimeZone], [DST]
FROM 
    [dbo].[ZipCodes]
SELECT GETDATE();

在代码中我使用秒表

string first = "", second = "", third="";
System.Diagnostics.Stopwatch swatch = new System.Diagnostics.Stopwatch();
swatch = new Stopwatch();
swatch.Start(); Dapper request;

然后

swatch.Stop();
first = swatch.Elapsed.ToString(@"m:ss.fff");
swatch = new Stopwatch();
swatch.Start();

等等

结果:(以毫秒为单位)

                       1000     10000      43200
-------------------------------------------------
EF                      107      1085       4527
Dapper                  139      1084       4036
Stored procedure        129      1089       4519
SQL query                 8        17         60

EF,DAPPER和存储过程之间的差异很小。为什么这样?

为什么SQL Server中的查询如此之快,并且来自代码的请求速度慢了15-70倍?

可以还是否?

使用EF展示性能问题的代码将不会与Dapper或Ado Sprocs神奇地运行更快。要解决绩效问题的底部,您需要调查并消除性能问题的原因。

在顶级,这些性能问题源于两个核心问题。

  • 加载太多数据。
  • 经常加载数据。

我要注意的关键事情:(作为开始,还有更多的项目,但这些都是最大的胜利)

  1. 懒惰的加载:这是代码从一组相关实体加载的地方,但是这样做,代码在初始加载后正在访问这些相关实体,导致每个相关实体都单独加载。

    • 狩猎方法:将SQL Profiler连接到仅您对应用程序的调试实例的数据库中。(即本地数据库)
    • 症状:在主查询后查询很多"选择顶部(1)..."以加载单个实体或收集。
    • 修复:快速修复是引入急切的负载(.Include())来加载这些集合。更好的解决方法是使用.Select()加载有关代码所需的属性。
  2. .ToList():放错位置的.ToList()呼叫会导致巨大的性能头痛作为系统成熟,因为开发人员遇到了通过调用.ToList解决的EF问题。当开发人员尝试在.Where().Select()表达式中调用方法时,这些都会出现。ef无法理解这些通过SQL等效,因此添加.ToList()将其转换为Linq2Object,并且" Ta-da"起作用!

    • 狩猎方法:查找.ToList()的实例,并标记您在.Select().Where()之前找到.ToList()的任何情况
    • 症状:删除额外的.ToList()会导致EF错误。
    • 修复:检查有问题的功能是否具有同等数字。一个常见的问题是使用DateTime功能,可以在dbfunctions中找到。在其他情况下,找到有问题的功能并为所选择的预期数据创建视图模型,然后创建一个属性以在视图模型中运行该函数。
  3. 客户端分页 实体:没有适当样本数据的发展的另一种罪过。编写了查询,该查询实际上有效地返回了所有数据,而无需考虑记录总数。数据在带有分页的网格中显示了客户端,该网格"有效",但确实很慢。当数据库中只有200行,但现在有50,000行爬行时,它可以正常工作。(并且只会变得更糟)

    • 狩猎方法:查看返回集合的任何API/控制器方法。这些查询是否使用.ToList().Skip() .Take()?这些方法会返回实体还是查看模型?
    • 症状:分页列表的负载确实很慢。加载后,切换页面很快。
    • 修复:应修改分页控件的调用以使用服务器端分页。这意味着将排序,页面大小和页面#信息发送到服务器调用。这样,可以使EF查询更有效地加载控件需要显示的行#。代码还应返回搜索结果的视图模型,该模型仅显示显示的列,以及按需加载完整实体所需的键。(例如,当用户单击以打开记录时。)当您只需要显示少数字段时,实体就可以重量级。
  4. 数据库索引:数据库是否已被监视和维护?是否有索引和索引维护?对于SQL Server,数据库是否已备份并拥有TX日志缩小?代码首先实现了这些问题,这些问题无需考虑备份数据库即可启用生命。随着系统的增长,支持它的数据库不小心。

    • 狩猎方法:您是否有一个专门的DBA,它正在照顾数据库,并清楚地表明它正在划痕?
    • 症状:数据库没有进行DBA或审查。该应用程序使用的是GUID PK,例如设置为NEWID()或Guid.New(),而无需任何索引维护。数据库没有设置索引。事务日志(.ldf)比数据库文件(.mdf)等大15倍。
    • 修复:聘请DBA。如果您使用的是GUID键,请切换到NewSequentionID()并建立一些计划的索引维护作业。

您在应用程序中获得的性能受到许多因素的影响,但是从数据库中获取数据,可以在3:

中拆分数据库
  • 查询运行时间
  • 数据传输时间
  • 客户处理时间

说,如果您的最高成本在数据库本身或数据传输中,则从Fe到Dapper或简单的数据标准中切换是没有意义的。但是,如果您的最高成本是在客户端应用中(高并发/小有效载荷/许多加入(LINQ)/多个数据集/多列列),则要从FE到Dapper或DataReaders进行切换是有道理的。但是,您需要了解,您将使用轻松的使用方式来换取性能。随着时间的流逝,关键应用程序在大多数现实世界中可能不会是有意义的。

最新更新