LINQ 消耗大量 CPU 资源
LINQ consumes a lot of CPU resources
下面的代码占了CPU的22%。
public async Task<Client> SingleByIdAsync(string clientId)
{
var baseQuery = _configurationDbContext.Clients.Where(p => p.ClientId == clientId).Include(p => p.ClientSecrets);
await baseQuery.SelectMany(p => p.Scopes).Include(p => p.ApiScope).LoadAsync();
return await baseQuery.SingleOrDefaultAsync();
}
LoadAsync
消耗了 CPU 的 8%。
而另一个函数消耗了 17% CPU:
public async Task<List<ApiResource>> FindByScopesNameAsync(List<string> scopes)
{
return await _configurationDbContext.ApiResources.Where(p => p.Scopes.Any(x => scopes.Any(y => y == x.ApiScope.Name))).Select(p => p).ToListAsync();
}
我的问题是这个 linq 有什么问题?为什么要占用这么多资源?我该如何优化它们?
CPU 不被 LINQ-query 本身消耗,而是被 Entity Framework 消耗,后者正在将您的结果加载到内存中。
将LINQ-Queries翻译成SQL。这里的性能开销可以忽略不计。
但是当你调用LoadAsync()
时,所有与Client
相关的ClientSecrets
、Scopes
和ApiScopes
都被加载到内存中。将大量数据加载到一个 DbContext
及其 ChangeTracker
会导致大量 CPU 负载。
因此,与其将所有内容都加载到内存中,不如尝试加载 Client
,然后仅加载与该客户端直接相关的 ApiScopes
。
此外,您可以全局关闭 ChangeTracking
,或添加 AsNoTracking()
扩展方法以进一步减少 CPU 您不需要更改跟踪的负载。
好的,所以我找到了一个非常好的解决方案。我这样做了:
services.AddDbContext<ConfigurationDbContext>(cfg =>
{
cfg.UseSqlServer(configuration.GetConnectionString("Default"), x =>
{
x.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery);
});
});
所以我只是将 QuerySplittingBehavior 设置为 SplitQuery。这个解决方案非常高效,我现在每秒可以处理 100 个受支持请求中的 1000 个(根据在 8 个线程上执行的加载测试)。
下面的代码占了CPU的22%。
public async Task<Client> SingleByIdAsync(string clientId)
{
var baseQuery = _configurationDbContext.Clients.Where(p => p.ClientId == clientId).Include(p => p.ClientSecrets);
await baseQuery.SelectMany(p => p.Scopes).Include(p => p.ApiScope).LoadAsync();
return await baseQuery.SingleOrDefaultAsync();
}
LoadAsync
消耗了 CPU 的 8%。
而另一个函数消耗了 17% CPU:
public async Task<List<ApiResource>> FindByScopesNameAsync(List<string> scopes)
{
return await _configurationDbContext.ApiResources.Where(p => p.Scopes.Any(x => scopes.Any(y => y == x.ApiScope.Name))).Select(p => p).ToListAsync();
}
我的问题是这个 linq 有什么问题?为什么要占用这么多资源?我该如何优化它们?
CPU 不被 LINQ-query 本身消耗,而是被 Entity Framework 消耗,后者正在将您的结果加载到内存中。
将LINQ-Queries翻译成SQL。这里的性能开销可以忽略不计。
但是当你调用LoadAsync()
时,所有与Client
相关的ClientSecrets
、Scopes
和ApiScopes
都被加载到内存中。将大量数据加载到一个 DbContext
及其 ChangeTracker
会导致大量 CPU 负载。
因此,与其将所有内容都加载到内存中,不如尝试加载 Client
,然后仅加载与该客户端直接相关的 ApiScopes
。
此外,您可以全局关闭 ChangeTracking
,或添加 AsNoTracking()
扩展方法以进一步减少 CPU 您不需要更改跟踪的负载。
好的,所以我找到了一个非常好的解决方案。我这样做了:
services.AddDbContext<ConfigurationDbContext>(cfg =>
{
cfg.UseSqlServer(configuration.GetConnectionString("Default"), x =>
{
x.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery);
});
});
所以我只是将 QuerySplittingBehavior 设置为 SplitQuery。这个解决方案非常高效,我现在每秒可以处理 100 个受支持请求中的 1000 个(根据在 8 个线程上执行的加载测试)。