【问题标题】:Issue with N+1 queries with accessors when using Lighthouse GraphQL and Laravel使用 Lighthouse GraphQL 和 Laravel 时使用访问器的 N+1 查询问题
【发布时间】:2021-07-16 22:47:32
【问题描述】:

我对旧的 N+1 问题有点意见。我正在运行 Lighthouse 4.8、Laravel 5.8,并在我的配置中将 batched_queriesbatchload_relations 设置为 true。我在我的架构中设置了 hasManyhasOne(匿名了一点):

type Team {
    id: Int!
    Y_resource_id: Int
    X_resource_id: Int
    name: String
    description: String
    start_date: Date
    cost: Float
    Y: Resource @hasOne
    X: Resource @hasOne
    members(
        where: _ @whereConditions
        hasDepartmentMember: _ @whereHasConditions(columns: ["z_id","start_date"])
        orderBy: [OrderByClause!] @orderBy
    ): [TeamMember] @hasMany @is_logged_in
    options: [TeamResourceOption] @hasMany
    optionIds: [Int!]
}

我正在使用多个利用这些关系的访问器函数,以及一些自定义解析器和本地范围。这些似乎是导致 N+1 问题的原因,例如,当我在 Team 中加载时,有 N+1 问题的 php 查询之一是这个(在 Team 模型上尝试加载加入并填充信息):

public function getHasTeamLeaderAttribute(): bool
{
    return $this->members()->teamLeaders()->count() > 0;
}

这是 members() 函数:

public function members(): HasMany

{
    return $this->hasMany(TeamMember::class);
}

以及 TeamMember 类:

public function scopeTeamLeaders($query)
{
    return $query->where('team_members.group_role', '=', TeamMember::ROLE_TEAM_LEADER);
}

Clockwork 将其显示为 2 N+1 个问题(我调用了两次):

N+1 queries: App\Models\Teams\Team::App\Models\Teams\TeamMember loaded 4 times.
Team.php:254
N+1 queries: App\Models\Teams\Team::App\Models\Teams\TeamMember loaded 2 times.
Team.php:254

我尝试在访问器中使用 loadMissing 来加载 teamLeaders 但这似乎增加了 N+1 查询,我还尝试使用 $with 我需要的关系加载模型,但这会使整个应用程序变慢(我假设是因为它现在会在不需要的地方自动加载它)。

这是一个继承的项目,我对 GraphQL 并不完全熟悉,所以任何指向正确方向的指针都会很棒。谢谢!

【问题讨论】:

    标签: laravel laravel-5 optimization graphql-js laravel-lighthouse


    【解决方案1】:

    我不确定这与 GraphQL 或 Lighthouse 有什么关系,因为您显示的类型似乎没有直接的 has_team_leader 属性...但是:

    再多的急切加载都无法解决这里的 N+1 问题。

    问题是这段代码:

    public function getHasTeamLeaderAttribute(): bool
    {
        return $this->members()->teamLeaders()->count() > 0;
    }
    

    因为它使用关系方法而不是访问器,所以它总是会执行查询,$this->members->count() > 0 可以在这里提供帮助(并且急切加载members),但这也不理想,也不使用teamLeaders() 范围。

    Laravel 有办法解决这个问题:Counting Related Models

    您可以使用它来预先加载团队负责人的数量,并在您的 getter 中使用它来检查。它可能看起来像这样:

    // where the query is executed:
    
    $teams->loadCount(['members as team_leaders_count' => function ($query) {
        $query->teamLeaders();
    }])
    
    // and in your getter:
    
    public function getHasTeamLeaderAttribute(): bool
    {
        // Use `team_leaders_count` if loaded, if not fallback to the non-optimal query
        return ($this->team_leaders_count ?? $this->members()->teamLeaders()->count()) > 0;
    }
    

    您也可以使用 @withCount 指示 Lighthouse 为您进行热切加载,但由于我没有看到架构中实际调用 has_team_leaders 的位置,所以我不知道在哪里添加它,但它可能是知道这是可能的很有用。

    【讨论】:

      猜你喜欢
      • 2018-05-20
      • 1970-01-01
      • 2021-06-04
      • 2017-12-04
      • 1970-01-01
      • 2019-11-28
      • 2011-08-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多