实体框架与存储过程 - 性能度量

Posted

技术标签:

【中文标题】实体框架与存储过程 - 性能度量【英文标题】:Entity Framework Vs Stored Procedures - Performance Measure 【发布时间】:2012-04-02 02:41:21 【问题描述】:

我试图确定实体框架比存储过程慢了多少。我希望说服我的老板让我们使用 Entity Framework 来简化开发。

问题是我进行了性能测试,看起来 EF 比 Stored Procs 慢了大约 7 倍。我觉得这非常难以置信,我想知道我是否遗漏了什么。这是一个决定性的测试吗?我可以做些什么来提高 EF 测试的性能?

        var queries = 10000;

        //  Stored Proc Test
        Stopwatch spStopwatch = new Stopwatch();
        spStopwatch.Start();
        for (int i = 0; i < queries; i++ )
        
            using (var sqlConn = new SlxDbConnection().Connection)
            
                var cmd = new SqlCommand("uspSearchPerformanceTest", sqlConn)  CommandType = CommandType.StoredProcedure ;

                cmd.Parameters.AddWithValue("@searchText", "gstrader");
                sqlConn.Open();
                SqlDataReader dr = cmd.ExecuteReader();

                List<User> users = new List<User>();
                while (dr.Read())
                
                    users.Add(new User
                    
                        IsAnonymous = Convert.ToBoolean(dr["IsAnonymous"]),
                        LastActivityDate = Convert.ToDateTime(dr["LastActivityDate"]),
                        LoweredUserName = dr["LoweredUserName"].ToString(),
                        MobileAlias = dr["MobileAlias"].ToString(),
                        UserId = new Guid(dr["UserId"].ToString()),
                        UserName = (dr["UserName"]).ToString()
                    );
                

                var username = users.First().UserName;
                sqlConn.Close();
            
        
        spStopwatch.Stop();
        Console.WriteLine("SP - 0 Queries took 1", queries, spStopwatch.ElapsedMilliseconds );

        //  EF  Test
        Stopwatch entityStopWatch = new Stopwatch();

        var context = new SlxDbContext();
        var userSet = context.Set<User>();
        entityStopWatch.Start();
        for (int i = 0; i < queries; i++)
        
            User user = userSet.Where(x => x.UserName == "gstrader").First();
        

        entityStopWatch.Stop();
        Console.WriteLine("Entity - 0 Queries took 1", queries, entityStopWatch.ElapsedMilliseconds);

结果:

SP - 10000 次查询耗时 2278

实体 - 10000 次查询耗时 16277

【问题讨论】:

我想补充一点,拆箱应该足够了,而不是明确地进行转换。这样做: (string)dr["MobileAlias"] 而不是 .ToString() 【参考方案1】:

您可以采取一些措施来优化您的查询。 Here on MSDN 你可以找到一个很好的概述。

但老实说,具有手动映射的存储过程在性能上总是会更快。但是问问自己,性能有多重要?在大多数项目中,开发时间比性能更重要。什么更难开发?带有解析的原始查询还是实体框架查询?

ORM 的设计不是因为它们的性能比手写方法好得多。我们使用它们是因为开发更容易!

如果您使用实体框架编写应用程序并将所有查询隐藏在存储库模式之后,您可以快速开发,然后当性能成为问题时,测量您的应用程序以检测瓶颈。那么也许你的一些查询需要优化,可以移动到存储过程和手动映射。

【讨论】:

您的用户会不同意您的观点,即开发人员的时间比他们的时间更重要……尤其是当他们坐着看那个旋转的沙漏时!也就是说,为什么不在 sprocs 中编写 sql 并使用 EF 调用它们 - 两全其美? 如果你有一个旋转的沙漏,你就有一个可以优化的真实场景。在我看来,要达到这种情况,开发人员的生产力比过早的优化更重要 多年来我一直在想关于存储库模式的真实示例。我现在有。谢谢你:) @Flater 它没有,这意味着归根结底所需的时间无关紧要。问一个 DBA,他们会告诉你如何正确地做到这一点。无论如何,您应该有一个 DBA 来做生产数据库。想象一下,如果您在代码更改中更改了数据库架构,例如添加新列或新索引。 Code First 有很多失败的方法。 @gbjbaanb 很抱歉,但我不同意。在应用程序每天更新多次的现代世界中,只有一个 DBA 触摸生产不再可行。实体框架迁移、SQL Server Data Tools 或 Red Gate 的工具都可以优化这些流程。这确实涉及 DBA,但也优化了整个流程。【参考方案2】:

同意@Wouter de Kort ... 此外,当您需要迁移到过程时,您可以将 EF 与过程结合使用,以帮助从一个迁移到另一个。

如果您将功能统一到设计良好的程序中,则在典型应用程序中迁移到程序会更快。即在 one sproc 调用中尽可能多地完成工作。例如,在购物车 MVC 应用程序中,当用户单击结帐按钮时,您可能将 ORM 用于:

    查找用户的身份验证(登录仍然有效吗?) 查看权限(他们可以购买上述物品吗?有什么特殊要求吗?) 查看库存数量以确保它们没有在处理过程中耗尽 在付款前写入数据库以保留(从可用库存中移除)项目 查找付款信息 记录...?

或者可能是完全不同的步骤,但无论如何,重点是,MVC 应用程序将使用 ORM 多次调用 DB 以进入下一步。

如果所有这些逻辑都封装在一个编写良好的存储过程中,那么只需调用一个存储过程就可以了。使用 MVC-ORM 路由,数据必须从 DB 复制到驱动程序并传递到 ORM(通常通过网络到不同的主机),然后由 MVC 应用程序读取以做出简单的决定,然后重复直到所有步骤完成.在使用封装该签出步骤的存储过程的情况下,需要完成的数据复制和移动、网络 IO、上下文切换等要少得多。

以这种方式考虑 MVC-ORM 解决方案。人“A”只知道事实,而人“B”完全有能力根据他没有提出的给定事实做出决定。人“B”通过电子邮件向“A”发送事实。根据“A”的回答,“B”可能会在做出决定之前要求更多的事实。这是很多来回的消息传递。

如果您有一个掌握所有事实和知识的人来做决定,您只需要提出一个问题,他们的大脑就会在内部处理所有事情并得出答案。不涉及与另一个人的商议。自然会更快。

这并不是说它一定更好。将事实与决策分开意味着这些组件是可单独替换/可测试的,但是,如果您已与 MVC 和 DB 结婚,那么这就是“非问题”。

另一方面,许多 MVC 爱好者讨厌编写 SQL,因此他们认为将任何决策逻辑放入 SQL 中是一场自然灾害。对于这些人来说,任何逻辑都必须使用MVC 使用的语言来编写,因为这样可以加快他们的开发速度。在某些情况下,这也是一个“非问题”,因为在某些 RDMBS 的情况下,您可以使用与 MVC 使用的语言相同的语言编写存储过程(例如:.Net - SQL Server 存储过程可以用 C# 编写并使用.Net;Postgresql 函数(无存储过程)可以用 Perl、Python、php 等编写编码已经很快了。

【讨论】:

我曾经将 C# 用于存储过程。糟糕的性能。 SQL 明显更快。我认为这与托管->非托管转换有关,但无论哪种方式:使用正确的工具完成工作,而不是“C#锤子” 如果性能是必要的,并且您不介意编写 C++,那么您可以用 C++ 编写扩展存储过程,它应该(如果做得好)胜过其他任何东西,它会比 #【参考方案3】:

我可以想到 2 个很好的理由,为什么我会选择存储过程而不是 ORM

    封装的优点有据可查。您永远不会创建一个类,然后期望该类的用户与该类的内部工作进行交互。如果事情发生了变化怎么办?如果你改变了一个变量类型怎么办?您怎么知道访问该变量的所有代码在哪里?解决方案,使用接口。你的数据库也不例外,它是一个对象,所以给它一个接口。对于 SQL Server 数据库,这意味着存储过程。

    作为一名 DBA 工作了 20 多年,我已经记不清有多少次开发人员将他们编写的代码问题描述为“数据库问题”,并建议“与 DBA 交谈”作为解决方案。现在,我不介意这个。当我纠正你的错误,而你没有纠正我的错误时……好吧,让我们说这是我在绩效评估期间肯定会提出的问题。但至少你能做的就是以一种能让我在最短的时间内修复你的错误的方式编写代码。这意味着,将您的代码放入存储过程中。当存储过程导致性能问题时,这对任何体面的 DBA 来说都是生计。但是当 ORM 引起性能问题时,这对任何人来说都是一场噩梦。当你的 DBA 试图帮助你时,至少给他一个战斗的机会。

如果您不能在存储过程中编写代码,或者您确实需要更长的时间来编写代码,那么您可能需要考虑转行。

【讨论】:

我是一名软件开发人员,而不是 dba。但我希望我能不止一次地对此表示赞同。【参考方案4】:

需要注意的是

从 .NET Framework 4.5 开始,LINQ 查询被缓存 自动。但是,您仍然可以使用编译的 LINQ 查询来降低以后执行的成本,并且编译的查询可以更多 比自动缓存的 LINQ 查询更高效。

From MSDN Compiled Queries (LINQ to Entities)

【讨论】:

以上是关于实体框架与存储过程 - 性能度量的主要内容,如果未能解决你的问题,请参考以下文章

实体框架与 NHibernate - 性能

带有存储过程的实体框架 VS LINQ to SQL VS ADO.NET? [关闭]

使用实体框架获取存储过程输出参数抛出映射错误:数据读取器与指定的不兼容

使用实体框架从存储过程中获取数据时的参数问题

存储过程参数名称和实体框架

在实体框架中使用存储过程