Entity Framework 6 - 查询性能

Posted

技术标签:

【中文标题】Entity Framework 6 - 查询性能【英文标题】:Entity Framework 6 - Query Performance 【发布时间】:2020-05-07 07:21:31 【问题描述】:

我使用 Entity Framework 6,我目前有一个包含许多包含的查询,它将大约 1200 个实体加载到 dbContext 中。加载实体似乎很慢,因为查询需要将近一分钟。我能对表演做些什么吗?我有 4 个这样的查询需要 2.5 分钟才能加载? LazyLoading 已启用,但出于性能原因,我预加载了实体。

var report = DbContext.REPORT.Single(r => r.ID == reportId);

//this query takes a bit less than 1 minute
DbContext.REPORT_ELEMENT
    .Include(re => re.LAYOUT)
    .Include(re => re.PAGEMASTER)
    .Include(re => re.REPORT_ELEMENTS)
    .Include(re => re.SUBTITLE_CONTENT)
    .Include(re => re.REPORT_ELEMENT_NOTE)
    .Include("SUBTITLE_CONTENT.CONTENT_ELEMENT.LANGUAGE")
    .Include("TITLE_CONTENT.CONTENT_ELEMENT.LANGUAGE")
    .Where(re => re.REPORT_ID == report.ID)
    .Load();

【问题讨论】:

考虑添加AsNotracking() 另一个选项可能是 EF Core 中的 docs.microsoft.com/en-us/ef/ef6/querying/raw-sql,您可以使用它,然后输入您的输入语句。如果这适用于 EF 中的输入语句,我不确定。 您的问题非常广泛,可能适用许多解决方案。通常为了查询性能,您需要在 SQL Server 上打开探查器,假设您正在使用 MSSQL。查看执行计划,然后尝试优化它。此外,延迟加载会降低性能而不是提高性能。索引可能会有所帮助,但在这种情况下我对此表示怀疑。此外,Select 也可能会有所帮助,因为您正在选择要提供的数据。使用分析器检查额外的查询 2.5 分钟是相当多的。 @panoskarajohn 我如何指定问题?你需要什么信息?我需要将 coden-p 的包含中列出的所有引用加载到 dbcontext 中,因为我需要它们来编写 xml。如果我一块一块地重新加载它们,整个过程会更慢 @CrazyEight 正如我在上面的 cmets 中提到的,查询的分析时间将是一个开始。执行计划将是一个很好的补充。也是。已经提供的答案也是有效的。尝试调试,看看发生了什么。可能有额外的查询正在发生。尝试禁用ProxiesLazyLoading,看看发生了什么。尝试调查。老实说,这些东西是反复试验。您提到您将所有数据放入 XML 中。您可能需要重新考虑这一点。所有数据都有价值吗? 【参考方案1】:

性能建议:

阻止跟踪。以只读模式查询。 防止在一次查询中获取过多数据。尝试分页。 阻止包含。该查询有太多的Includes,这会降低性能。

阻止跟踪

考虑添加AsNoTracking,这样可以提高查询性能。

参考:https://docs.microsoft.com/en-us/ef/core/querying/tracking#no-tracking-queries

只获取你需要的数据

查询速度慢的主要原因是它输出了太多数据。考虑添加:Take(200)Skip() 以仅获取您需要的数据或当前页面需要的数据。使用寻呼机生成报告。这可能会有很大帮助。

阻止Include

Include 生成 SQL 以选择多个表。这大大增加了复杂性。只能选择自己需要的数据,防止写Include函数。

例如,如果你只想拿到盒子里的最后一个球,可以考虑这样写:

public class Box

    public int Id  get; set; 
    public IEnumerable<Ball> Balls  get; set; 


public class Ball

    public int Id  get; set; 

    public int BoxId  get; set; 
    public Box Box  get; set; 

var boxes = await Boxes
            // DO NOT Call Include(t => t.Balls) here!
            .Where(somecondition)
            .Select(t => new Box()
              Id = t.Id,
              Balls = t.Balls.OrderByDescending(x => x.CreationTime)
                         .Take(1) // Only get what you need
            )               
            .ToListAsync()

此外,当我们使用 Select 时,我们可以删除 .Include,因为它不会在这里产生任何影响。

【讨论】:

但是我想一次加载所有数据,因为我之后创建了一个 xml 文件。如果我一点一点地加载数据,就会触发很多查询,这会使性能完全变慢。 AsNoTracking() 似乎让整个事情变得更慢【参考方案2】:

免责声明:我是项目的所有者Entity Framework Plus

Query IncludeOptimized 功能允许使用 include 进行过滤并同时优化查询性能。

它通常会提高性能(将查询拆分为更小的查询)

DbContext.REPORT_ELEMENT
    .IncludeOptimized(re => re.LAYOUT)
    .IncludeOptimized(re => re.PAGEMASTER)
    .IncludeOptimized(re => re.REPORT_ELEMENTS)
    .IncludeOptimized(re => re.SUBTITLE_CONTENT)
    .IncludeOptimized(re => re.REPORT_ELEMENT_NOTE)
    .IncludeOptimized(re => re.SUBTITLE_CONTENT.Select(sc => sc.CONTENT_ELEMENT))  // SelectMany?
    .IncludeOptimized(re => re.SUBTITLE_CONTENT.Select(sc => sc.CONTENT_ELEMENT).Select(ce => ce.LANGUAGE)) // SelectMany?
    .IncludeOptimized(re => re.TITLE_CONTENT)
    .IncludeOptimized(re => re.SUBTITLE_CONTENT.Select(sc => sc.CONTENT_ELEMENT)) // SelectMany?
    .IncludeOptimized(re => re.SUBTITLE_CONTENT.Select(sc => sc.CONTENT_ELEMENT).Select(ce => ce.LANGUAGE)) // SelectMany?
    .Where(re => re.REPORT_ID == report.ID)
    .Load();

文档:EF+ Query IncludeOptimized

【讨论】:

【参考方案3】:

除了来自 Anduin 的建议之外,我还想添加建议以将 Includes() 拆分为几个不同的查询。 EF 将能够跟踪同一 DBContext 内的实体之间的引用。作为一般经验法则 - 不要在同一查询中使用超过三个 Includes()。还要确保您在数据库中为每个生成的 JOIN 都有一个索引。

为此,您必须将实体中的 FK 字段另外公开给导航属性。

您的初始查询会变成这样:

    DbContext.LAYOUT
        .Where(re => re.LAYOUT_ID == report.LAYOUT_FK)
        .Load();
    DbContext.PAGEMASTER
        .Where(re => re.PAGEMASTERT_ID == report.PAGEMASTER_FK)
        .Load();

【讨论】:

以上是关于Entity Framework 6 - 查询性能的主要内容,如果未能解决你的问题,请参考以下文章

Entity Framework 4 中的 Linq 查询。糟糕的性能

Entity Framework Core 性能优化

Entity Framework Core 性能优化

Entity Framework Core 性能优化

《Entity Framework 6 Recipes》中文翻译系列 (13) -----第三章 查询之使用Entity SQL

Entity Framework 6 查询时上下文重复