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相关的ClientSecretsScopesApiScopes都被加载到内存中。将大量数据加载到一个 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 个线程上执行的加载测试)。