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

rea*_*ers 58 stored-procedures entity-framework performance-testing database-performance

我正在尝试确定实体框架比存储过程慢多少.我希望说服我的老板让我们使用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);
Run Code Online (Sandbox Code Playgroud)

结果:

SP - 10000查询占用了2278

实体 - 10000查询占用16277

Wou*_*ort 82

您可以执行一些操作来优化查询.在MSDN上,您可以找到一个很好的概述.

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

ORM的设计并不是因为它们比手写方法表现得更好.我们使用它们因为开发更容易!

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

  • 如果你有一个旋转沙漏,你就有了一个可以优化的真实场景.为了实现这种情况,我认为开发人员的工作效率比过早的优化更为重要 (34认同)
  • 你的用户会不同意你的看法,开发人员的时间比他们更重要...特别是当他们坐着看着旋转的沙漏时!也就是说,为什么不在sprocs中编写sql并使用EF调用它们 - 两者都是最好的? (19认同)
  • 多年来我一直想知道存储库模式的reallife示例.我现在有.谢谢 :) (2认同)

Mik*_*keM 10

与@Wouter de Kort达成协议......此外,当您需要转移到程序时,您可以将EF与程序结合使用以协助从一个程序迁移到另一个程序.

如果您将功能统一到设计良好的过程中,那么在典型应用程序中迁移到过程会更快.即尽可能多地在一个 sproc调用中完成工作.例如,在用户单击结帐按钮的购物车MVC应用程序中,您可以将ORM用于以下内容:

  1. 查找用户的身份验证(登录是否仍然有效?)
  2. 查找权限(他们可以购买所述商品吗?,有特殊要求吗?)
  3. 查找库存数量以确保其过程中没有耗尽
  4. 在付款之前写入数据库以保留(从可用库存中删除)项目
  5. 查找付款信息
  6. 伐木......?

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

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

以这种方式考虑MVC-ORM解决方案.人"A"只知道事实,而"B"则具有所有精明的知识,可以用他没有提出的既定事实作出决定.人"B"以事实形式发送电子邮件"A".根据"A"的答案,"B"可能会在做出决定之前要求更多的事实.这是很多来回传递的消息.

如果你有一个人拥有所有事实和知识来做出决定,你只需要提出一个问题,他们的大脑会在内部处理所有事情以得出答案.不涉及与他人的商议.当然它会更快.

这并不是说它必然更好.将事实与决策分开意味着这些组件可以单独替换/测试,但是,如果您与MVC和您的数据库结婚,那么这是一个"非问题".

另一方面,许多MVC粉丝讨厌编写SQL,因此他们考虑将任何决策逻辑放入SQL作为自然灾害.对于这样的人来说,必须使用与MVC相同的语言编写任何逻辑,因为它加速了它们的开发.在某些情况下,这也是一个"非问题",因为在某些RDMBS的情况下,您可以使用与MVC使用的语言相同的语言编写sprocs(示例:.Net - SQL Server sprocs可以用C#编写并使用.Net; Postgresql函数(没有sprocs)可以用Perl,Python,PHP等编写.)这种情况下的优点是你可以拥有快速的sprocs,它在一次调用中封装了多个步骤,你可以使用你的编程语言已经快速编码了.

  • 我曾经使用过C#的sprocs ......糟糕的表现.SQL明显更快.我认为它与托管 - >非托管转换有关,但无论哪种方式:使用正确的工具来完成工作,而不是"C#hammer" (3认同)
  • 如果性能是必要的并且您不介意编写 C++,那么您可以在 C++ 中编写扩展存储过程,它应该(如果做得正确)击败其他任何东西,并且它会比 # 更加精简 (2认同)

Dan*_*iel 6

重要的是要注意

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

来自 MSDN 编译查询 (LINQ to Entities)


小智 6

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

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

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

如果您无法在存储过程中进行编码,或者您确实需要花费很长时间才能做到这一点,那么您可能需要考虑改变职业。