Linq嵌套列表关联查询性能问题及优化方案咨询
Linq 大数量查询性能优化方案
核心问题排查:现有写法触发N+1数据库查询,每遍历1条OtherTranslations记录就会单独发起1次关联表查询,数据量越大耗时增长越快。
优化方向如下:
- 替换嵌套子查询为一次性关联查询
先批量拉取所有需要的关联数据,再在内存中分组构造结果,仅需1次数据库交互,改写示例:
// 单次查询拉取所有关联数据,避免高频IO交互 var allData = context.OtherTranslations .AsNoTracking() .Join(context.OtherTranslationsValues, ot => ot.ID, otv => otv.OtherTranslationId, (ot, otv) => new { ot, otv }) .Join(context.Languages, temp => temp.otv.LanguageId, l => l.ID, (temp, l) => new { temp.ot.ID, temp.ot.TranslationCode, LangCode = l.Code, LangValue = temp.otv.Value }) .ToList(); // 内存分组构造最终返回结构 var u = allData .GroupBy(x => new { x.ID, x.TranslationCode }) .Select(g => new CmsOtherTranslationsView { Id = g.Key.ID, TranslationCode = g.Key.TranslationCode, LangValues = g.Select(x => new LangCodeValues { LangCode = x.LangCode, LangValue = x.LangValue }).ToList() }).ToList();
- 关闭非必要实体跟踪,如果你不需要修改查询返回的实体,在查询前加上
AsNoTracking(),可以降低EF的实体状态维护开销,上面的示例已经包含该配置。 - 数据库层添加索引,给
OtherTranslationsValues.OtherTranslationId、OtherTranslationsValues.LanguageId、Languages.ID这几个关联字段添加索引,大幅提升关联查询的执行速度。 - 超大数据量采用分批加载,不要一次性拉取全量数据,通过
Skip()、Take()方法分页查询,既降低单次数据库压力,也避免内存溢出风险。 - 禁止在Linq投影的Select内部调用
ToList(),该写法会强制每条父记录触发单独的数据库查询,是触发N+1问题的直接原因。
内容的提问来源于stack exchange,提问作者jskorups
相关产品推荐
相关产品推荐

