【问题标题】:Why is this Entity Framework code faster than its native SQL equivalent? (in MySQL)为什么这个实体框架代码比它的原生 SQL 代码快? (在 MySQL 中)
【发布时间】:2013-10-18 11:05:13
【问题描述】:

我在我的 C# .NET 应用程序中运行 Entity Framework 5,针对使用 MySQL .NET 连接器 6.6.5 的 MySQL 数据库。

通常,当 EF 不够快时,我会使用存储过程或使用 context.Database.SqlQuery 直接执行 SQL 但是,我最近遇到了一个问题,即 SQL 调用实际上比它的 EF 调用花费的时间要长得多,我想知道如果有人知道这是为什么?

这是(慢)SQL 查询:

public sbyte? getFirstRouteTypeFromStop(string primaryCode) {

    string sql = string.Format("SELECT r.route_type FROM stoptimes st INNER JOIN trips t ON st.trip_id = t.trip_id INNER JOIN routes r ON t.route_id = r.route_id WHERE st.stop_id = '{0}' LIMIT 1;", primaryCode);
    return context.Database.SqlQuery<sbyte?>(sql).FirstOrDefault();

}

这是(快速)EF 代码:

public sbyte? getFirstRouteTypeFromStop(string primaryCode) {

    return context.stoptimes.Where(st => st.stop_id.Equals(primaryCode)).FirstOrDefault().trip.route.route_type;

}

这个方法在循环中被重复调用,EF 快了很多。 (至少 1000%) 为什么?

重要提示:

  • MySQL 数据库对所有这些列进行了适当的索引。
  • 当本机 SQL 查询直接在 MySQL 中运行时,它的执行速度似乎比在 C# 应用程序中运行时快得多 - 我怀疑这是一个非常重要的观察结果。

【问题讨论】:

  • 你看过EF生成的sql吗?可以发一下吗?
  • 你应该跟踪数据库语句,看看 EF 对你的代码做了什么。
  • 这不是一个快速的跟踪,因为那里有一个.FirstOrDefault(),我猜它必须破坏IQueryable,并且可能意味着它被分成2个语句? (这是猜测)如果我有时间并且没有人可以给我一个快速的答案,我会关闭我的 MySQL 服务器以打开查询日志并发布结果。
  • 最可能的原因是String.format函数。因为这个函数支持格式化输出,所以它需要更多的时间来分析完整的输入并在需要的地方替换
  • 只需在 IQueryable 上调用 .ToString() 即可查看生成的 SQL 语句。你不需要对你的服务器做任何事情

标签: c# mysql .net sql entity-framework


【解决方案1】:

您在 SqlQuery 中使用了 INNER JOIN,这意味着:

选择的总行数 =(停留时间的行数)*(路线中的行数)*(行程中的行数)

然后在那个巨大的列表中执行“WHERE st.stop_id = '{0}'”... 我怀疑这是 sql 查询中的问题...内部联接进行了大量选择并从中过滤记录...

而 EF 代码仅在表停止时间上过滤

Where(st => st.stop_id.Equals(primaryCode))

所以它可以快速过滤,然后在单个选定的记录上,获取路线和行程。

注意:- 尝试使用 LEFT OUTER JOIN...这将使您的查询更快。

希望对你有帮助……

【讨论】:

  • 感谢 SQL 提示,但问题是 - 尽管这会稍微提高性能 - 现有查询在直接在 MySQL 本身中执行时已经运行得非常快(请参阅我的“重要说明”的第 2 部分) .只有在 EF 中运行时才会变慢。
【解决方案2】:

也许您已经在内存中拥有全部或部分Stoptimes(及其导航属性,如triproute)。如果是这种情况,EF 只会点击一次数据库(或者至少不是在循环的每次迭代中)。使用SQLQuery,您可以在循环的每次迭代中访问数据库。也许primaryCode 在循环中重复了很多?或者您可以在应用程序的先前代码中检索 Stoptimes 而无需处理上下文?

【讨论】:

  • 嗯,这是一个有趣的观点。实际上,我确实每次都在循环中重复使用相同的上下文,所以也许 EF 正在做一些优化并将我没想到的东西保存在内存中。
  • 我确信 MySQL 必须存在一个分析器,当您执行应用程序时,您可以在其中查看所有数据库命中。
【解决方案3】:

尝试更改 TSQL

SELECT r.route_type 
  FROM stoptimes st 
  JOIN trips t 
    ON st.trip_id = t.trip_id 
   AND st.stop_id = '{0}'
  JOIN routes r 
    ON t.route_id = r.route_id 
 LIMIT 1;

【讨论】:

  • 感谢 SQL 提示,但问题是 - 尽管这会稍微提高性能 - 现有查询在直接在 MySQL 本身中执行时已经运行得非常快(请参阅我的“重要说明”的第 2 部分) .只有在 EF 中运行时才会变慢。
  • 你需要发布来自 EF 的两个 TSQL
猜你喜欢
  • 2014-02-19
  • 2015-03-26
  • 2012-04-15
  • 1970-01-01
  • 1970-01-01
  • 2011-08-22
  • 2023-03-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多