如何优化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
相关产品推荐
相关产品推荐

