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

Laravel处理200万+行大表时,按月/按周统计出现内存超限、加载过慢如何优化

性能问题根源

你当前代码查询慢、触发内存限制的核心原因有4个:

  • 近3个月的统计没有在数据库层完成聚合计算,直接把全量符合条件的POI记录全部拉取到PHP内存中,实例化为Eloquent模型后再用集合方法分组计数。当POI表数据量超过10万行时,仅模型实例的内存占用就会直接超过PHP的内存限制
  • 原代码用whereYear/whereMonth对created_at字段做函数处理,MySQL无法命中索引,必须全表扫描判断
  • 存在冗余无意义操作:拉取全量数据后调用了sortBy排序,但后续逻辑根本没有用到排序结果,白白消耗CPU和内存资源
  • 存在拼写bug:空数据分支里把pois写成了pots,会触发未定义数组键的运行异常

优化步骤

1. 把聚合计算全部下推到数据库层

所有计数、分组逻辑全部交给MySQL执行,PHP侧只接收最终的统计结果,单查询的内存占用可以从全量行级别的MB级开销降到KB级

当月POI计数优化

替换掉会导致索引失效的whereYear/whereMonth写法,用时间范围的闭区间判断:

// 取当月1号的零点时间作为起始值
$currentMonthStart = Carbon::now()->startOfMonth();
$data['pois']['total'] = PoiLocation::where('created_at', '>=', $currentMonthStart)
    ->where('created_at', '<', Carbon::now())
    ->count();

对字段加函数判断的查询写法会让MySQL放弃普通索引的使用,必须逐行扫描判断,数据量越大速度下降越明显。

近3个月ISO周维度统计优化

直接在SQL层用MySQL内置的YEARWEEK函数(参数传3时完全匹配ISO周计算规则)完成分组计数,完全不需要拉取全量数据到PHP:

// 近3个月的时间边界
$statStart = Carbon::now()->subMonths(3);
// 数据库层完成分组、计数,只返回每个周对应的统计值
$data['pois']['weeks'] = PoiLocation::query()
    ->where('created_at', '>=', $statStart)
    ->selectRaw('YEARWEEK(created_at, 3) as iso_week, COUNT(*) as week_count')
    ->groupBy('iso_week')
    ->pluck('week_count', 'iso_week')
    ->toArray();

注意:原代码用Carbon的isoWeek()取的是不带年份的周数,跨年度的3个月统计里会把不同年份的同周数数据合并到同一个key里,导致统计错配。MySQL的YEARWEEK(时间,3)生成的是带年份的周标识,更符合长期统计的逻辑

2. 数据库索引配套优化

给created_at字段增加单列索引,保障上述范围查询、分组聚合的执行效率:

ALTER TABLE `poi_locations` ADD INDEX `idx_created_at` (`created_at`);

索引生效后,上述两个查询的扫描范围会从全表缩小到对应时间区间的索引范围,通常查询速度可以提升100倍以上。

3. 修正原代码的逻辑bug

原代码计算最高周数的分支存在拼写错误,补上修正后的兜底逻辑:

if (!empty($data['pois']['weeks'])) {
    $data['pois']['high'] = max($data['pois']['weeks']);
} else {
    $data['pois']['high'] = 1;
}

可选进阶优化

如果后续统计周期继续拉长,可以增加一张按周预聚合的统计表,提前把每周的POI计数结果存在表里,每次新增数据时同步更新预聚合值,统计查询时直接读取预聚合表的结果,性能可以再提升一个台阶。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.22 09:36:04