【问题标题】:why this simple linq query takes so much time?为什么这个简单的 linq 查询需要这么多时间?
【发布时间】:2011-03-28 19:43:53
【问题描述】:

我有这个 linq 查询:

 public static Banner getSideBarBanner(){
    DataClassesDataContext db = new DataClassesDataContext();
    var bannerSiderBar = (from b in db.Banners
                  where b.Position.Equals(EBannersPosition.siderbar.ToString())
                  && b.Visible == true
                  select b).FirstOrDefault();
    return bannerSiderBar;
}

好吧,我使用 dotTrace 来分析应用程序,我发现查询执行需要很长时间(超过 2 秒)

我只是想知道,为什么要花这么多时间,尤其是当我的 Banner 表有大约 30 条记录时!!!

提前感谢您的意见...

更新: Banner的表格架构:

更新 2:如果我使用简单的 SQL 连接而不是 linq,查询执行需要 700 毫秒,这是一个巨大的改进......

 public static Banner getSideBarBanner()
{
    Banner bannerFound = new Banner();
    SqlConnection myConnection = new SqlConnection(ConfigurationManager.ConnectionStrings["Library_prodConnectionString"].ConnectionString);
    try
    {
        myConnection.Open();
        SqlCommand myCommand = new SqlCommand("SELECT path, link FROM Banner b WHERE b.Position = @position AND b.Visible = 1 ", myConnection);
        myCommand.Parameters.Add(new SqlParameter("@position", EBannersPosition.siderbar.ToString()));
        SqlDataReader myReader = myCommand.ExecuteReader();
        while (myReader.Read())
        {
            if (myReader["path"] != null)
                bannerFound.Path = myReader["path"].ToString();
            if (myReader["link"] != null)
                bannerFound.Link = myReader["link"].ToString();
        }
        myConnection.Close();
    }
    catch (Exception e)
    {
        CreateLogFiles Err = new CreateLogFiles();
        Err.ErrorLog(HttpContext.Current.Server.MapPath("~/Site/Logs/ErrorLog"), e.ToString());
    }
    return bannerFound;
}

这告诉我,将 linq 查询转换为 sql 的性能很差……你怎么看?

【问题讨论】:

  • 如果不了解您的数据库、架构、网络配置(可能从您到数据库有很多延迟)等,这是无法判断的。
  • 你也用过SQL Server Profiler吗?
  • @Robert Harvey:不,没有索引位置,但是当我有这么小的桌子时,这有关系吗?
  • 也许我们可以看到 dotTrace 的其余部分?
  • @Robert Harvey:我已经用更详细的图像替换了初始分析图像......

标签: c# asp.net linq


【解决方案1】:

您应该考虑试用http://l2sprof.com/(如果您使用 LINQ to SQL)或http://efprof.com/(如果您使用实体框架)并使用它来确定您的查询正在生成什么 SQL。

它们都可以免费使用 30 天,我希望有足够的时间来解决这个问题。 ;)

Robert 在 cmets 中指出的另一种可能性是设置 Log property on your DataContext,它将生成的 SQL 输出到您想要的任何位置。

您也可以只使用 SQL Server Profiler,它可能会显示比您需要的更多信息,但是嘿,它可能仍然可以完成工作。

【讨论】:

  • 虽然您当然可以获得这些工具之一来获取生成的 SQL,但 it's not necessary to do so.
  • @Robert:嘿,这可能比使用 SQL Server Profiler 更容易。我会将其添加到我的答案中。谢谢!
【解决方案2】:

请记住,LINQ 会延迟执行,直到您枚举结果。在这种情况下,当您调用 FirstOrDefault 时,它实际上正在运行数据库查询,这可能解释了延迟。

不是FirstOrDefault耗时2s,而是整个查询耗时。

考虑到这一点,如果您希望人们进一步缩小范围,您需要发布您的架构、数据等。

【讨论】:

  • 虽然延迟执行确实会影响分析结果,但您的回答并没有真正说明查询性能不佳的原因。
  • @Robert:我想这是真的,但我想指出FirstOrDefault 本身可能不是罪魁祸首,而是一般的查询。
【解决方案3】:

首先,调用 FirstOrDefault() 所花费的时间是将 Linq 表达式树消化为 SQL、将该 SQL 发送到数据库、在结果集中检索结果以及将该结果集映射到对象所花费的时间。这可能需要一段时间。

其次,我会分析数据库。进行跟踪并找出为此调用发送到数据库的确切 SQL 语句。如果该语句不包含 FirstOrDefault 限制的表示,例如SELECT TOP 1 ...,那么您将所有记录拉出表只是为了丢弃除其中一个之外的所有记录。 Linq2SQL 应该比这种情况下更聪明;如果没有,请考虑升级到 MSEF 或 NHibernate(我承认,对于一个查询来说这是一项大工作,但如果此语句不能产生有效的查询,那么任何类似的查询也不会有效)。

【讨论】:

  • 查询是一个简单的查询,据推测它要从中提取的表中只有 30 条记录,因此很难想象这个查询需要 2 秒才能执行。显然,OP 没有告诉我们一些关键信息..
  • 我同意,但从他已经所说的来看,这就是答案;要么没有有效地构建查询,要么他没有在数据层投入足够的硬件来有效地检索结果。
【解决方案4】:

将索引添加到Position,然后试试这个:

 public static Banner getSideBarBanner()
 {
    DataClassesDataContext db = new DataClassesDataContext();

    string thisPosition = EBannersPosition.siderbar.ToString();

    var bannerSiderBar 
        = db.Banners.FirstOrDefault<Banner>
             (x => x.Position == thisPosition && x.Visible);

    return bannerSiderBar;
}

基本上这里的想法是:

  1. FirstOrDefault 放在前面并使其成为强类型,然后
  2. 去掉EBannersPosition.siderbar.ToString()的多次执行,

【讨论】:

  • @Robert Harvey:谢谢,我已将索引添加到 Position 并用您的方法替换了我的方法,而且...仍然几乎相同的时间量(执行为 2.167)...只是很奇怪...
  • @Cristian - 尝试一次删除一部分查询,看看它们是否有明显的不同。我将首先删除FirstOrDefault,方法是在FirstOrDefault 之前调用ToList()。如果这没有影响,请尝试一次从 Where 子句中删除条件。
  • @Chris: ToList() 将立即执行该部分查询,但仅返回完整列表并查看需要多长时间可能很有用。如果返回 30 条记录仍然需要 2 秒,那么...
  • @Robert - 是的,我的想法是大部分时间似乎都被 L2S 占用,实际上是从表达式树创建查询,所以也许制作一个“笨蛋”实际上会更快L2S查询,让应用代码找到合适的记录。
  • 700 毫秒对于这样一个简单的查询来说仍然是永恒的。你运行这些东西的那台机器有多健康?
【解决方案5】:

在我看来,您所看到的是 dotTrace 的问题。它报告与 Linq-To-Sql 相关的任何事情的夸大时间。 (在 Best .NET memory and performance profiler? 上查看我的 cmets)它并不是唯一存在此问题的分析产品。

我自己也经历过这种情况,并且只是在过程的最后阶段才尝试使用 System.Diagnostics.StopWatch 验证 dotTrace 的时间。当然,分析器无法像秒表那样报告准确的计时。但是它们相差很大(某些因素)并且完全歪曲了您的代码所花费的时间(对于 L2S 部分)。

总执行时间比实际 SQL Server 工作时间多几个因素这一事实在一定程度上证明了这一点。

但请记住,Linq-To-Sql (L2S) 本身会产生一些开销,这有时会很重要。 L2S 为每个数据行创建对象并不像在对象上调用构造函数并填充其属性那么简单。不仅因为 Model 类不仅仅是简单的对象,还因为它对模式和数据类型等进行了大量验证。

当然,查询本身的编译可能需要相当长的时间。

因此,总而言之,尝试使用 StopWatch 来获取时间。如果您能验证我关于 dotTrace 的声明,如果您能分享一些结果,那就太好了。

UPDATE1:您可以尝试的一件事是,不要在第一次运行时,而是在第二次运行代码时进行计时。只是为了确保您不会遇到任何一次性成本。 此外,使用 Linq,您始终可以选择使用已编译的查询。稍微搜索一下。但根据我的经验,您会得到同样不准确的结果。

关于已编译查询的注意事项 - 如果不是绝对必要,请勿使用它们。它们有一些主要缺点,如果您正在寻找的全部是简单的 ORM。一是,你失去了身份跟踪。此外,您不能将它们用于 WHERE expr IN (setexpr) 类型的查询 (list.Contains(...)。当然,另一个是可读性。 最后,如果您打算使用它们,您可能需要查看 Nexterday 的 L2S 自动编译 (http://linqautocompiler.codeplex.com/)

【讨论】:

  • 这也是我的感觉...当我使用探查器 dotTrace ON 启动站点时,加载大约需要 16 秒。如果不进行分析,大约需要 5-6 秒。
  • 这本身就是分析器的预期行为。代码将运行更长时间,因为毕竟在进行分析时会发生很多事情。
  • Profilers 会考虑这种开销并尝试生成准确的时间。问题是,根据我的经验,对于 L2S,时间是不正确的。所以百分比也是错误的,导致图片误导
  • 您可以尝试下一个版本的 dotTrace 的最新每晚:confluence.jetbrains.net/display/NetProf/… 我刚刚做了一个快速测试,结果似乎很好 - 我不确定,因为我没有任何真正有意义的代码。
猜你喜欢
  • 2011-08-27
  • 2012-10-21
  • 2018-01-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-27
相关资源
最近更新 更多