【发布时间】:2023-02-02 12:56:28
【问题描述】:
我正在使用EF Core 7,看起来自EF Core 5以来,现在有Single vs Split Query执行。
我看到默认配置仍然使用单一查询执行。
我注意到在我的日志中它说:
Microsoft.EntityFrameworkCore.Query.MultipleCollectionIncludeWarning': 编译加载多个相关集合的查询 集合导航,通过“包含”或通过投影,但是 没有配置“QuerySplittingBehavior”。默认情况下,实体 框架将使用“QuerySplittingBehavior.SingleQuery”,它可以 可能会导致查询性能下降。
然后我在数据库上下文中配置了一个警告以获取更多详细信息:
services.AddDbContextPool<TheBestDbContext>( options => options.UseSqlServer(configuration.GetConnectionString("TheBestDbConnection")) .ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.MultipleCollectionIncludeWarning)) );然后我能够具体地看到哪个调用实际上导致了该警告。
var user = await _userManager.Users .Include(x => x.UserRoles) .ThenInclude(x => x.ApplicationRole) .ThenInclude(x => x.RoleClaims) .SingleOrDefaultAsync(u => u.Id == userId);所以基本上相同的代码就像:
var user = await _userManager.Users .Include(x => x.UserRoles) .ThenInclude(x => x.ApplicationRole) .ThenInclude(x => x.RoleClaims) .AsSplitQuery() // <=== .SingleOrDefaultAsync(u => u.Id == userId);使用
Split查询选项。我通过 documentation 了解了这一点,但仍然不确定是否可以从中创建一个模式。 我想将最常见的设置为整个项目的默认值,并且只在特定场景下使用另一个。
根据文档,我觉得“Split”通常应该作为默认值使用,但要谨慎。我还注意到他们针对分页的文档,上面写着:
使用 Skip/Take 拆分查询时,请特别注意使查询顺序完全唯一;不这样做可能会导致返回不正确的数据。例如,如果结果仅按日期排序,但同一日期可能有多个结果,则每个拆分查询都可能从数据库中获得不同的结果。按日期和 ID(或任何其他唯一属性或属性组合)排序使排序完全唯一并避免此问题。请注意,关系数据库默认情况下不应用任何排序,即使在主键上也是如此。
这完全有意义,因为查询将被拆分。
但是,如果我们主要从数据库中获取单个记录,无论包含列表及其导航属性有多大或多小,我是否应该始终采用“拆分”方法?
我很想听听是否有任何最佳实践,
when使用which方法。谢谢
【问题讨论】:
-
我的经验法则是:在遇到性能问题之前,请采用安全的默认设置。然后剖析和优化值得优化的部分。
-
这是一个写得很好的问题的例子。它帮助社区提高他们的技能。谢谢你。
-
我会分析每个用户的 ApplicationRole 和 RoleClaims 记录的分布。如果每条记录大约有 5-10 条记录,那么 Single 可能不会有任何问题。我会将
SingleOrDefault替换为FirstOrDefault,因为前者导致select top 2而不是select top 1。