使用实体框架提高大型查询的性能 [重复]

Posted

技术标签:

【中文标题】使用实体框架提高大型查询的性能 [重复]【英文标题】:Improve performance for large queries with Entity Framework [duplicate] 【发布时间】:2017-10-06 13:57:21 【问题描述】:

我有一个基于价格进行大量计算的应用程序,目前大约需要 87 秒。根据时间线,我怀疑我使用包含的方式对于 Entity Framework 来说不是最理想的,而且我可以做出重大改进。

我的时间线是这样的:

0 秒 - 接收 GET 请求 2 秒 - 向 SQL Server 发送查询(查询包含所有必要的 incudes, 结果是> 40.000个字符。我在 问题的底部) 5 秒 - 从 SQL Server 接收查询结果 45 一些线程正在退出 46 一些线程正在退出 50 一些线程正在退出 80 关闭SQL连接(我 认为将查询结果映射回我的 C# 类需要 EF6 ~75 秒,但我不确定) 80 次起始计算 87 所有计算完成

查询大多是这样设计的

var result = await db.MyAuthorizedFooExtension()
    .Include(x => x.Foofie)
    .Include(x => x.Bar.Baz)
    .Include(x => x.FooFoo.Select(y => y.BarBar))
    .Include(x => x.FooFoo.Select(y => y.BazBaz))
//etc.
    .ToListAsync()

结果如下:

[UnionAll7].[C9] AS [C55], 
...
   FROM  (SELECT 
        CASE WHEN ([Join2].[ID1] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1], 
        [Extent1].[ID] AS [ID], 
...
        [Join2].[ID1] AS [ID5], 
...
        CAST(NULL AS varchar(1)) AS [C4], 
...
        FROM   [dbo].[Contract] AS [Extent1]
        INNER JOIN [dbo].[Leverancier] AS [Extent2] ON [Extent1].[Leverancier] = [Extent2].[ID]
        LEFT OUTER JOIN  (SELECT [Extent3].[ID] AS [ID1], [Extent3].[Contract] AS [Contract], [Extent3].[ContractStatusOud] AS [ContractStatusOud], [Extent3].[ContractStatusNieuw] AS [ContractStatusNieuw], [Extent3].[MutatieDatumTijd] AS [MutatieDatumTijd], [Extent3].[Medewerker] AS [Medewerker], [Extent3].[Status] AS [Status1], [Extent3].[LastModifiedDate] AS [LastModifiedDate1], [Extent3].[LastModifiedBy] AS [LastModifiedBy1], [Extent4].[ID] AS [ID2], [Extent4].[Naam] AS [Naam], [Extent4].[WindowsNaam] AS [WindowsNaam], [Extent4].[Rol] AS [Rol], [Extent4].[Unit] AS [Unit], [Extent4].[Status] AS [Status2], [Extent4].[LastModifiedDate] AS [LastModifiedDate2], [Extent4].[LastModifiedBy] AS [LastModifiedBy2]
            FROM  [dbo].[ContractWijzigingHistorie] AS [Extent3]
            INNER JOIN [dbo].[Medewerker] AS [Extent4] ON [Extent3].[Medewerker] = [Extent4].[ID] ) AS [Join2] ON [Extent1].[ID] = [Join2].[Contract]
        WHERE ( EXISTS (SELECT 
            1 AS [C1]
            FROM [dbo].[Contract] AS [Extent5]
            WHERE ( EXISTS (SELECT 
                1 AS [C1]
                FROM [dbo].[Leverancier] AS [Extent6]
                WHERE [Extent6].[ID] = [Extent5].[Leverancier]
            )) AND ([Extent5].[ID] = [Extent1].[ID])
        )) AND ([Extent1].[Status] IN (1)) AND ((DATEPART (year, [Extent1].[Startdatum])) <= @p__linq__0) AND ((DATEPART (year, [Extent1].[Einddatum])) >= @p__linq__1)
    UNION ALL
        SELECT 
        2 AS [C1], 
        [Extent7].[ID] AS [ID], 
...
        CAST(NULL AS int) AS [C9], 
        CAST(NULL AS datetime2) AS [C10], 
...
        [Extent9].[PeriodeWaarde] AS [PeriodeWaarde], 
...
        [Project9].[LastModifiedBy] AS [LastModifiedBy], 
...
        [UnionAll5].[ID2] AS [C60], 
...
        CAST(NULL AS int) AS [C62], 
...
etc.

如何在仍然使用 Entity Framework 的同时提高性能?

【问题讨论】:

您至少应该include the actual Execution Plan,您可以使用Paste the Plan 并在您的问题中分享链接。另外try to read it yourself,也许您可​​以通过查询找出性能问题。 是使用视图还是 UDF 选项? @Igor 我现在就制定计划 @DiskJunky 我不明白为什么不 如果性能至关重要,那么尽可能接近 SQL 将是可行的方法。设置好视图或 UDF 后,您可以像表格一样将其添加到实体框架中并从那里使用它。它还使您可以更好地控制查询计划以更好地构建查询 【参考方案1】:

这引出了一个问题,在得到查询结果后你在做什么?如果您读出您的查询,您将看到您在急切地加入相关表的同时获得了所有结果。

如果您在从查询中检索数据后过滤此数据,那么您应该以在具体化数据之前过滤数据的方式编写查询(调用 ToList)。您还需要调用相关表吗?您还应该只从数据库中选择您知道您将使用的列。

现在,再次了解您希望您的方法做什么会更容易。创建一个新对象或匿名对象以供选择:

var result = await db.MyAuthorizedFooExtension()
//etc.
.Select(s => new

    Id = s.Id,
    FooName = s.FooName,
    BazId = s.Bar.Baz.Id

.ToListAsync();

并在最后实现。如果您在可选方案中需要包含,请将查询设为 IQueryable,执行您的逻辑并在最后具体化。

【讨论】:

查询后有很多复杂的计算(太多,无法在问题中发布)。我想我只需要每个表中大约 80% 的列,所以那里可能有一些改进的空间,但我认为我认为我可以用更少的努力和更多的结果进行更大的优化。物化查询是一个有意识的决定,从物化列表运行所有后续计算比在 IQueryable 上进一步构建所有后续步骤要快得多(我之前尝试过) 我建议您做的第一件事是优化您从查询中检索到的数据。确保您没有调用您知道不需要的加入表。您还可以查看执行计划,了解哪些调用花费的时间最多。您还可以尝试将索引添加到您正在查询的表中,看看这是否会缩短执行时间。 好点 :) 但我很肯定我需要我加入的所有表格。我一定会研究执行计划 即使您需要通过预先加载加入的表,您也不必预先加载。通过在具体化之前将数据选择到对象中,您将不会加载所有表中的所有记录,而只会加载您需要的记录。您将(我相信)还可以使用 LINQ 的 Where 操作来仅选择您需要的内容,我认为您无法在 Include 中执行此操作。显然,在您尝试使用您选择的记录的情况下,延迟加载将启动并对数据库执行更多调用,这也可能产生负面影响。请记住。

以上是关于使用实体框架提高大型查询的性能 [重复]的主要内容,如果未能解决你的问题,请参考以下文章

使用实体框架最大化性能 [重复]

实体框架查询中新类名和新类名()之间的区别[重复]

如何在实体框架查询中使用多表达式方法[重复]

LIKE查询与实体框架[重复]

实体框架:查询子实体 [重复]

实体框架查询以获取子项[重复]