如何提高使用多个包含的 EF Core 查询的性能
How to improve performance of an EF Core query which uses several Includes
我有这个查询,为简洁起见我将对其进行简化:
public IQueryable<User> GetByIdAsync(Guid userId)
{
return MyContext
.Users
//Bunch of Includes
//Most of which have a ThenInclude
//Followed by another ThenInclude
.FirstOrDefaultAsync(u => u.Id == userId)
}
当 运行 大约 100 个用户时,需要超过 15 秒(运行在我的机器上本地运行)。不太好。
我试过使用 AsNoTracking()
,并将其更改为使用编译查询,如下所示:
private static Func<MyContext, Guid, Task<User>> _getByIdAsync =
EF.CompileAsyncQuery((MyContext context, Guid userId) =>
context
.Users
//Same Includes as above
.Where(u => u.Id == userId)
.FirstOrDefault());
public IQueryable<User> GetByIdAsync(Guid userId)
{
return await _getByIdAsync(userId);
}
仍然没有区别。
我已经查看了相关主题的这个答案,建议使用普通的旧 SQL:
我看过这个答案,其中提到了聚簇索引:
我当然不能排除任何 Includes
因为客户端依赖于所有这些信息。重新设计也不是现阶段的选择。
问题
- 是否有任何其他选项可以提高性能?
- 我在我的任何子 table 索引中都看不到任何 CLUSTERED 或 NONCLUSTERED 标签。这值得研究吗?如果值得,是否可以向我指出任何说明我如何使用 EF(或不使用 EF)进行更新的文档?
你有很多方法,但这完全取决于。
- 你有
.FirstOrDefaultAsync(u => u.Id == userId)
这意味着对于 100 个用户你将访问数据库 100 次所以总共 15 000 / 100 == 等于每个请求 150 毫秒。要改进它,请尝试使用 in
子句(如 .Where(u=> userIds.contains(u.Id))
一次获取所有 100 个用户
示例。
private static Func<MyContext, Guid, Task<List<User>>> _getByIdAsync =
EF.CompileAsyncQuery((MyContext context, List<Guid> userIds) =>
context
.Users
//Same Includes as above
.Where(u => userIds.Contains(u.Id))).ToListAsync();
- 我对你的数据结构一无所知,但如果你可以使用连接编写 linq,它可能会更快,因为对于一个请求中的多对多,EF 每次依赖项都可以访问数据库。
示例如何使用连接进行查询
var query = (from users in context.Users
join otherTable in context.OtherTable on users.Id equals otherTable.UserId).ToList();
- Ef 尝试适应通用目的,但有时只有你知道你的数据,你才能做得更好,当我有存储库方法来逐一获取数据时,我曾经遇到过与你类似的问题,但后来我写了新的使用数组获取数据的方法,该方法负责连接数据,通过 EF 基本上不可能快速完成。 所以我要说的是 在一个请求中一对一地加载所有内容,然后从数据库读取并使用另一个查询去获取你需要的多对多。
- 也可以得到sql查询
您可以使用此示例获得 sql
public IQueryable<User> GetByIdAsync(Guid userId)
{
var = query = MyContext
.Users
//Bunch of Includes
//Most of which have a ThenInclude
//Followed by another ThenInclude
var sql = query.ToSql(); // <--------------------------- sql query
return query.FirstOrDefaultAsync(u => u.Id == userId)
}
并使用 sql 查询来分析并查看其是否使用索引。
最后我真的很讨厌这样的方法 public IQueryable GetByIdAsync(Guid userId) 问题是大多数时候你不需要所有的东西,但是你开始越来越多地使用它们并变得依赖它们...这就是为什么我建议使用没有存储库模式的 EF,EF 本身是存储库,仅从您需要的数据库中获取数据。
我有这个查询,为简洁起见我将对其进行简化:
public IQueryable<User> GetByIdAsync(Guid userId)
{
return MyContext
.Users
//Bunch of Includes
//Most of which have a ThenInclude
//Followed by another ThenInclude
.FirstOrDefaultAsync(u => u.Id == userId)
}
当 运行 大约 100 个用户时,需要超过 15 秒(运行在我的机器上本地运行)。不太好。
我试过使用 AsNoTracking()
,并将其更改为使用编译查询,如下所示:
private static Func<MyContext, Guid, Task<User>> _getByIdAsync =
EF.CompileAsyncQuery((MyContext context, Guid userId) =>
context
.Users
//Same Includes as above
.Where(u => u.Id == userId)
.FirstOrDefault());
public IQueryable<User> GetByIdAsync(Guid userId)
{
return await _getByIdAsync(userId);
}
仍然没有区别。
我已经查看了相关主题的这个答案,建议使用普通的旧 SQL:
我看过这个答案,其中提到了聚簇索引:
我当然不能排除任何 Includes
因为客户端依赖于所有这些信息。重新设计也不是现阶段的选择。
问题
- 是否有任何其他选项可以提高性能?
- 我在我的任何子 table 索引中都看不到任何 CLUSTERED 或 NONCLUSTERED 标签。这值得研究吗?如果值得,是否可以向我指出任何说明我如何使用 EF(或不使用 EF)进行更新的文档?
你有很多方法,但这完全取决于。
- 你有
.FirstOrDefaultAsync(u => u.Id == userId)
这意味着对于 100 个用户你将访问数据库 100 次所以总共 15 000 / 100 == 等于每个请求 150 毫秒。要改进它,请尝试使用in
子句(如.Where(u=> userIds.contains(u.Id))
一次获取所有 100 个用户
示例。
private static Func<MyContext, Guid, Task<List<User>>> _getByIdAsync =
EF.CompileAsyncQuery((MyContext context, List<Guid> userIds) =>
context
.Users
//Same Includes as above
.Where(u => userIds.Contains(u.Id))).ToListAsync();
- 我对你的数据结构一无所知,但如果你可以使用连接编写 linq,它可能会更快,因为对于一个请求中的多对多,EF 每次依赖项都可以访问数据库。
示例如何使用连接进行查询
var query = (from users in context.Users
join otherTable in context.OtherTable on users.Id equals otherTable.UserId).ToList();
- Ef 尝试适应通用目的,但有时只有你知道你的数据,你才能做得更好,当我有存储库方法来逐一获取数据时,我曾经遇到过与你类似的问题,但后来我写了新的使用数组获取数据的方法,该方法负责连接数据,通过 EF 基本上不可能快速完成。 所以我要说的是 在一个请求中一对一地加载所有内容,然后从数据库读取并使用另一个查询去获取你需要的多对多。
- 也可以得到sql查询
您可以使用此示例获得 sql
public IQueryable<User> GetByIdAsync(Guid userId)
{
var = query = MyContext
.Users
//Bunch of Includes
//Most of which have a ThenInclude
//Followed by another ThenInclude
var sql = query.ToSql(); // <--------------------------- sql query
return query.FirstOrDefaultAsync(u => u.Id == userId)
}
并使用 sql 查询来分析并查看其是否使用索引。
最后我真的很讨厌这样的方法 public IQueryable GetByIdAsync(Guid userId) 问题是大多数时候你不需要所有的东西,但是你开始越来越多地使用它们并变得依赖它们...这就是为什么我建议使用没有存储库模式的 EF,EF 本身是存储库,仅从您需要的数据库中获取数据。