我有一个如下的EF核心查询:
var existingViolations = await _context.Parent
.Where(p => p.ProjectId == projectId)
.Include(p => p.Relation1)
.Include(p => p.Relation2)
.ThenInclude(r => r.Relation21)
.Include(p => p.Relation3)
.AsSplitQuery()
.ToListAsync();此查询通常需要55-65秒,有时会导致数据库超时。查询中包含的所有表,包括父表,都包含30k-60k行和3-6列。我尝试使用LoadAsync()将其拆分为更小的查询,如下所示:
_context.ChangeTracker.LazyLoadingEnabled = false;
_context.ChangeTracker.AutoDetectChangesEnabled = false;
await _context.Relation1.Where(r1 => r1.Parent.ProjectId == projectId).LoadAsync();
await _context.Relation2.Where(r2 => r2.Parent.ProjectId == projectId).Include(r2 => r2.Relation21).LoadAsync();
await _context.Relation3.Where(r3 => r3.Parent.ProjectId == projectId).LoadAsync();
var result = await _context.Parent.Where(p => p.ProjectId == projectId).ToListAsync();这样可以节省大约5秒的查询时间,所以没有什么值得夸耀的。我已经做了一些计时工作,这是最后一行(var result = await _context.Parent.Where(p => p.ProjectId == projectId).ToListAsync();),完成时间最长,约占所用时间的90%。
我如何进一步优化这一点?
编辑:下面是生成的SQL查询:
SELECT [v].[Id], [v].[Description], [v].[ProjectId], [v].[RuleId], [v].[StateStatus], [v0].[Id], [v0].[ElementId], [v0].[Role], [v0].[ParentId], [t].[Id], [t].[ActivatedDate], [t].[StateStatus], [t].[ParentId], [t].[Id0], [t].[RunId], [t].[SerializedState], [t].[StateId], [p].[Id], [p].[ActualValue], [p].[CurrentValue], [p].[ParameterId], [p].[ParentId]
FROM [Parent] AS [v]
LEFT JOIN [Relation1] AS [v0] ON [v].[Id] = [v0].[ParentId]
LEFT JOIN (
SELECT [s].[Id], [s].[ActivatedDate], [s].[StateStatus], [s].[ParentId], [s0].[Id] AS [Id0], [s0].[RunId], [s0].[SerializedState], [s0].[StateId]
FROM [Relation2] AS [s]
LEFT JOIN [Relation21] AS [s0] ON [s].[Id] = [s0].[StateId]
) AS [t] ON [v].[Id] = [t].[ParentId]
LEFT JOIN [Relation3] AS [p] ON [v].[Id] = [p].[ParentId]
WHERE [v].[ProjectId] = @__projectId_0
ORDER BY [v].[Id], [v0].[Id], [t].[Id], [t].[Id0], [p].[Id]当直接在数据库中运行SQL查询时,大约需要3-4秒才能完成,因此问题似乎在于EF如何处理结果。
发布于 2022-01-10 20:40:41
没有看到真实的实体,也没有看到它们是如何配置的,这是任何人的猜测。
一般来说,当我看到像这样的性能问题时,我想要解决的第一件事是“加载这些数据是为了什么?”通常,当我看到使用大量Include的查询时,这类似于要为基于所选数据的视图或计算加载读取操作。如果您确实只需要每个表中的几个列来满足您的需求,那么投影到一个更简单的模型在这里会有很大的帮助。在相关数据中使用Select来填充视图的DTO/ViewModel类或为计算填充匿名类型的投影的好处是,Include希望在一次执行中为所有迫切加载的表传递 all 列,其中投影只会传回引用的列。在表可以包含您根本不需要的大型文本/二进制列之类的内容时,这一点非常重要。在数据库服务器可能与消费客户端或web服务器有一定距离的情况下,这一点也非常重要。尽管现在的问题听起来像DB查询本身,但导线上的数据较少=性能更快。
接下来要检查的是EF与表设计中所有表与任何相关配置之间的关系。等待一分钟从30-60k行中提取几个记录是荒谬的,我非常怀疑一些不使用FKs/索引的错误关系映射。另一个要查看的地方是针对数据库运行分析器以捕获正在运行的确切SQL语句,然后手动执行这些语句以调查它们的执行计划,这可能会揭示模式问题或实体关系映射产生非常低效率查询的一些奇怪之处。
下一件要检查的事情是使用一个消除的过程,看看是否有坏的关系。逐个消除每个急切的load Include语句,并查看每个查询场景所需的时间。如果有一个特定的Include负责大幅度的慢下来,钻研这种关系,看看为什么会这样。
这应该给你一些检查的途径。考虑修改您的问题与实际实体和任何进一步的疑难解答结果。
https://stackoverflow.com/questions/70653480
复制相似问题