c# - 多次使用 Include() 时 Entity Framework 代码很慢

标签 c# sql-server entity-framework linq iframe

我一直在调试一些缓慢的代码,似乎罪魁祸首是下面发布的 EF 代码。在稍后阶段评估查询需要 4-5 秒。我试图让它在 1 秒内运行。

我已经使用 SQL Server Profiler 对此进行了测试,似乎执行了一堆 SQL 脚本。它还确认在 SQL 服务器完成执行之前需要 3-4 秒。

我已经阅读了有关使用 Include() 的其他类似问题,并且在使用它时似乎确实存在性能损失。我尝试将以下代码拆分为几个不同的查询,但没有太大区别。

知道如何让以下内容更快地执行吗?

目前,我正在开发的网络应用程序在等待以下完成时只显示一个空的 iframe。如果我无法获得更快的执行时间,我必须将其拆分并使用数据部分加载 iframe 或使用另一个异步解决方案。这里的任何想法也将不胜感激!

using (var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted }))
        {
            formInstance = context.FormInstanceSet
                                .Includes(x => x.Include(fi => fi.FormDefinition).Include(fd => fd.FormSectionDefinitions).Include(fs => fs.FormStateDefinitionEditableSections))
                                .Includes(x => x.Include(fi => fi.FormDefinition).Include(fd => fd.FormStateDefinitions))
                                .Includes(x => x.Include(fi => fi.FormSectionInstances).Include(fs => fs.FormFieldInstances).Include(ff => ff.FormFieldDefinition).Include(ffd => ffd.FormFieldMetaDataDefinition).Include(ffmdd => ffmdd.ComplexTypePropertyNames))
                                .Include(x => x.CurrentFormStateInstance)      
                                .Include(x => x.Files)
                                .FirstOrDefault(x => x.FormInstanceIdentifier == formInstanceIdentifier);

            scope.Complete();
        }

最佳答案

tl;博士 多个 Include s 炸毁 SQL 结果集。很快,通过多个数据库调用加载数据而不是运行一个大型语句会变得更便宜。尝试找到 Include 的最佳组合和 Load声明。

it does seem that there is a performance penalty when using Include


这是轻描淡写!多个 Include s 迅速在宽度和长度上炸毁 SQL 查询结果。这是为什么?Include增长因子秒
(这部分适用于 Entity Framework classic、v6 及更早版本)
假设我们有
  • 根实体Root
  • 父实体Root.Parent
  • 子实体Root.Children1Root.Children2
  • LINQ 语句 Root.Include("Parent").Include("Children1").Include("Children2")

  • 这将构建一个具有以下结构的 SQL 语句:
    SELECT *, <PseudoColumns>
    FROM Root
    JOIN Parent
    JOIN Children1
    
    UNION
    
    SELECT *, <PseudoColumns>
    FROM Root
    JOIN Parent
    JOIN Children2
    
    这些 <PseudoColumns>由类似 CAST(NULL AS int) AS [C2], 的表达式组成并且它们在所有 UNION 中具有相同数量的列-ed 查询。第一部分为 Child2 添加伪列, 第二部分为 Child1 添加伪列.
    这就是 SQL 结果集大小的含义:
  • SELECT中的列数子句是四个表中所有列的总和
  • 行数是包含的子集合中记录的总和

  • 由于数据点总数为 columns * rows , 每增加一个 Include以指数方式增加结果集中的数据点总数。让我通过 Root 来证明这一点再次,现在有一个额外的 Children3收藏。如果所有表都有 5 列和 100 行,我们得到:
    Include ( Root + 1 个子集合):10 列 * 100 行 = 1000 个数据点。
    两个Include s(Root + 2 个子集合):15 列 * 200 行 = 3000 个数据点。
    Include s(Root + 3 个子集合):20 列 * 300 行 = 6000 个数据点。
    与 12 Includes这将达到 78000 个数据点!
    相反,如果分别获取每个表的所有记录而不是 12 Includes , 你有 13 * 5 * 100数据点:6500,不到10%!
    现在这些数字有些夸大了,因为这些数据点中有许多是 null ,因此它们对发送到客户端的结果集的实际大小贡献不大。但是查询优化器的查询大小和任务肯定会因 Include 的数量增加而受到负面影响。 s。
    平衡
    所以使用 Includes是数据库调用成本和数据量之间的微妙平衡。很难给出一个经验法则,但现在您可以想象,如果超过 ~3 Includes,数据量通常很快就会超过额外调用的成本。对于子集合(但对于父集合 Includes 多得多,这只会扩大结果集)。
    选择Include 的替代方案是在单独的查询中加载数据:

    context.Configuration.LazyLoadingEnabled = false;
    var rootId = 1;
    context.Children1.Where(c => c.RootId == rootId).Load();
    context.Children2.Where(c => c.RootId == rootId).Load();
    return context.Roots.Find(rootId);
    
    这会将所有需要的数据加载到上下文的缓存中。在此过程中,EF 执行关系修复,通过加载的实体自动填充导航属性(Root.Children 等)。最终结果与带有 Include 的语句相同s,除了一个重要的区别:子集合在实体状态管理器中没有标记为已加载,因此如果您访问它们,EF 将尝试触发延迟加载。这就是为什么关闭延迟加载很重要的原因。
    实际上,您必须弄清楚 Include 的哪个组合和 Load陈述最适合你。
    其他需要考虑的方面
    每个Include还增加了查询的复杂性,因此数据库的查询优化器将不得不越来越多地努力寻找最佳查询计划。在某些时候,这可能不再成功。此外,当某些重要索引丢失(尤其是外键)时,添加 Include 可能会影响性能。 s,即使有最好的查询计划。
    Entity Framework 核心
    笛卡尔爆炸
    出于某种原因,上述行为,UNIONed 查询,从 EF 核心 3 开始被放弃。它现在构建一个带有连接的查询。当查询为“星形”1 时,这会导致笛卡尔爆炸(在 SQL 结果集中)。我只能找到一个 note announcing this breaking change ,但没有说明原因。
    拆分查询
    为了应对这种笛卡尔爆炸,Entity Framework core 5 引入了 split queries 的概念。允许在多个查询中加载相关数据。它可以防止构建一个庞大的、成倍增加的 SQL 结果集。此外,由于较低的查询复杂性,即使多次往返,它也可以减少获取数据所需的时间。但是,并发更新时可能会导致数据不一致。

    1 查询根之外的多个 1:n 关系。

    关于c# - 多次使用 Include() 时 Entity Framework 代码很慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34724196/

    相关文章:

    sql-server - SQL Server 2005 事务日志条目 : LOP_Format_Page

    php - 使用中间表从另一个表获取信息

    entity-framework - EF Core 2.1.2 和 .net core 2.0 - Linq2Entities 导致 'must be reducible node' 错误

    entity-framework - EF 4.1模型第一个代码生成工具或模板

    c# - 使用 NEST 使用 linq 查询 Elasticsearch

    c# - 使用 MVVM 模式(特别是使用 MVVM Light),如何实现不仅仅是基元的模型?

    c# - 用于过滤导航属性和集合的 Linq 动态表达式

    SQL Server 2008 VarChar(Max) 仅返回 8000 个字符。为什么?

    entity-framework - 如何在尝试 Skip() 和 Take() 之前检查 IQueryable<T> 是否应用了 OrderBy

    c# - 从 Dapper QueryMultiple 中的第一个选择访问结果(多对多)