You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何优化Job的handle函数,避免统计各州城市区域数量时出现重复查询

问题分析

原代码存在多类冗余/重复查询问题,会随着州的数量增加出现线性的性能下降:

  • 循环内重复执行State::pluck('id')->toArray(),该查询在外层已经执行过,循环内的赋值完全无效,且每次循环多触发1次全表查询
  • 每次循环单独查询当前州的名称,有多少个州就会多触发多少次State表单条查询
  • 每次循环单独查询对应州的城市ID、对应城市的区域ID,有多少个州就会多触发2*N次City、Area表查询,数据量稍大就会产生严重的性能问题
优化方案

方案1:关联预加载(推荐,仅需3次查询,符合Laravel优雅写法)

需要先在模型中定义关联关系(如果还未定义的话):

State模型添加关联:

public function cities()
{
    return $this->hasMany(City::class, 'stateId');
}

City模型添加关联:

public function areas()
{
    return $this->hasMany(Area::class, 'cityId');
}

优化后handle代码:

public function handle()
{
    // 一次预加载所有州、对应城市、对应区域,总共仅3次查询
    $states = State::with('cities.areas')->get(['id', 'name']);
    
    foreach ($states as $state) {
        $cityCount = $state->cities->count();
        $areaCount = $state->cities->sum(fn($city) => $city->areas->count());
        Log::info("{$state->name} had {$cityCount} cities and {$areaCount} areas");
    }
}

方案2:无关联批量查询(无需修改模型,仅需3次查询)

如果不想调整模型关联,可以用批量查全量数据再分组统计的方案:

public function handle()
{
    // 1次查询所有州的id和名称
    $states = State::get(['id', 'name'])->keyBy('id');
    // 1次查询所有城市,按州id分组
    $cities = City::all(['id', 'stateId'])->groupBy('stateId');
    // 1次查询所有区域,按城市id分组
    $areas = Area::all(['id', 'cityId'])->groupBy('cityId');

    foreach ($states as $stateId => $state) {
        $stateCities = $cities->get($stateId, collect());
        $cityCount = $stateCities->count();
        $areaCount = $stateCities->sum(fn($city) => $areas->get($city->id, collect())->count());
        Log::info("{$state->name} had {$cityCount} cities and {$areaCount} areas");
    }
}

两种方案都把原来的4N+1次查询(N为州的数量)优化为固定3次查询,性能提升非常明显。

内容的提问来源于stack exchange,提问作者kareem

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 20:06:03