【问题标题】:Need help optimizing loop with Linq需要帮助使用 Linq 优化循环
【发布时间】:2011-03-01 22:02:35
【问题描述】:

免责声明:我对 linq 几乎没有经验。

我的工作任务之一是维护一个电子商务网站。昨天,我们的一位客户开始抱怨当他们尝试为谷歌创建提要文件时会发生超时。事实证明,如果用户有超过 9,000 个项目要放入他们的提要文件中,我们的代码至少需要一分钟才能执行。

我无法通过运行调试器找到问题的根源,所以我启动了一个分析器 (ANTS) 并让它完成它的工作。它找到了我们问题的根源,一个包含一些 linq 代码的 foreach 循环。代码如下:

var productMappings = GoogleProductMappingAccess.GetGoogleProductMappingsByID(context, productID);
List<google_Category> retCats = new List<google_Category>(numCategories);
int added = 0;

//this line was flagged by the profiler as taking 48.5% of total run time
foreach (google_ProductMapping pm in (from pm in productMappings orderby pm.MappingType descending select pm))
{
    if (pm.GoogleCategoryId.HasValue && pm.GoogleCategoryId > 0)
    {
        //this line was flagged as 36% of the total time
        retCats.Add(pm.google_Category);
    }

    else if (pm.GoogleCategoryMappingId.HasValue && pm.GoogleCategoryMappingId > 0)
    {
        retCats.Add(pm.google_CategoryMapping.google_Category);
    }
    else
    {
        continue;
    }

    if (++added >= numCategories)
    {
        break;
    }
}

你们有经验的开发者有什么想法吗?我正在尝试用 sql 替换所有 linq,但我不确定这是否是这里最好的做法(如果它是用 linq 编写的,那肯定是有原因的)。

【问题讨论】:

  • 不要在循环中执行 linq。将结果存储在变量中并使用它。
  • 提高 DataContext.CommandTimeout 以快速修复。
  • 可能不再需要这样做了。如果可能的话,喜欢避免它
  • @mad:这根本不会有任何区别。

标签: c# linq optimization


【解决方案1】:

如果您可以过滤掉您不想要的结果,您的查询应该会更快 - 您使用的是 orderby,因此所有这些结果都会在您的查询中耗尽处理,因为它们都必须进行评估:

 productMappings.Where( pm => (pm.GoogleCategoryMappingId.HasValue
                                && pm.GoogleCategoryMappingId > 0)
                              ||(pm.GoogleCategoryMappingId.HasValue && 
                                 pm.GoogleCategoryMappingId > 0)
                      )
                .OrderBy(...)

您还应该限制查询返回的结果数量,因为您最多只能使用numCategories。所以添加一个

.Take(numCategories)

到您的查询,而不是在 foreach 循环中检查。

【讨论】:

  • 我要试试这个,让你知道会发生什么
  • 添加的 where 子句将超时从 48% 提高到 78%。拍摄似乎也没有多大作用。
  • 那张桌子有多大? MappingType(以及 where 子句中的其他列)是否有适当的索引?
  • 该表有 411,086 个条目。 MappingType 好像没有索引,我也没有创建索引的权限。
  • 如果您没有适当的索引,则优化该查询的选项非常有限 - 它可能总是会导致全表扫描
【解决方案2】:

retCats.Add(pm.google_Category); 需要这么长时间的原因是因为您引用了一个延迟加载的对象,该对象会再次往返服务器。 如果您可以重构它,以便只获取 Id 的本地副本而不是整个对象,它将加快该部分的速度。

如果您确实需要获取整个对象,请研究在获取 productMappings 时如何在单个查询中将其拉下。如何做到这一点取决于您在 SQL 上使用的 LINQ 包装器。

【讨论】:

  • 一旦我解决了第一个瓶颈,我会进一步研究这个问题。谢谢
【解决方案3】:

不知道您的数据库架构真的很难说。几个想法:

1) 通过数据库引擎优化顾问运行查询。也许查询需要一些索引?

2) 预处理此信息并将其放入另一个表或文件中。这样当谷歌请求它时它不会超时。

【讨论】:

    【解决方案4】:

    这应该可行:

    var productMappings = GoogleProductMappingAccess.GetGoogleProductMappingsByID(context, productID);
    var categories = from pm in productMappings
                     where pm.GoogleCategoryId > 0 ||
                           pm.GoogleCategoryMappingId > 0
                     orderby pm.MappingType descending
                     select pm.google_Category ??
                            pm.google_CategoryMapping.google_Category;
    
    return categories.Take(numCategories);
    

    如果 GetGoogleProductMappingsByID 返回IQueryable(如果适用),效果最好。如果是这样,LINQ 会将整个语句转换为 T-SQL 命令,这将比内存中的 LINQ 快得多。

    您可以随意将 .ToList() 添加到最后一条语句,以使其与您的代码中的返回类型相同(并强制执行 LINQ 语句)。

    同时检查 .HasValue 和 > 0 是没有用的。检查 Id > 0 就足够了。
    欲了解更多信息:http://msdn.microsoft.com/en-us/library/2cf62fcy.aspx(运营商)

    【讨论】:

    • 我现在就试试。同意.hasvalue。表中的所有内容都是 1 或 0
    • 编译错误:查询正文必须以 select 子句或 group 子句结尾。奇怪的是我看到了 select 子句,所以我不知道是什么让编译器抱怨
    • 啊,我认为输入“desc”而不是“descending”是我的错误。抱歉,我在没有 Visual Studio 的情况下输入了这个,所以没有语法验证:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-24
    • 1970-01-01
    • 2017-04-25
    • 2011-05-14
    • 2023-01-27
    • 1970-01-01
    相关资源
    最近更新 更多