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

如何优化Laravel商品折扣接口 解决商品列表查询响应过慢问题

性能瓶颈核心原因

原代码存在典型的N+1查询问题,是高耗时的核心诱因:

  • 遍历全量商品时,每轮循环都会发起2次独立数据库查询:1次查询用户信息、1次查询当前商品分组对应用户的折扣规则
  • 按366件商品计算,接口总共会执行1(商品列表查询) + 366*2 = 733次数据库IO请求,数据库连接、查询的开销叠加直接把响应时间拉到秒级
  • 次要损耗:循环内重复执行路径生成逻辑,无差别返回表全量字段带来额外序列化开销

可直接上线的优化代码

优化核心思路:把循环内的重复查询全部提到循环外一次性执行,所有折扣匹配、价格计算全部在内存中完成,把总数据库查询次数从700+降到3次以内。

public function apiindex($userid)
{
    // 仅查询1次用户信息,移出循环逻辑
    $user = User::where('bayikodu', $userid)->first();
    // 提前处理用户不存在的边界场景,避免后续逻辑报错
    if (!$user) {
        return response()->json([], 403);
    }

    // 一次性拉取该用户所有分组的折扣规则,按键值对存储方便后续匹配,仅1次查询
    $iskontoMap = $user->iskontolar()->pluck('iskonto', 'grup_kodu')->toArray();

    // 查询商品时按需选取列表所需字段,避免返回无用字段增加传输、序列化开销
    $urunler = Stok::where('StokMiktari', '>', 0)
        ->get(['SiraNo', 'SatisFiyati', 'file_path', /* 此处补充商品列表实际需要的其他字段 */]);

    // 内存遍历计算,无额外数据库查询
    $urunler->transform(function (Stok $stok) use ($iskontoMap) {
        $fiyati = $stok->SatisFiyati;
        // 直接从预加载的折扣映射表取对应费率,无匹配则默认0
        $iskonto_orani = $iskontoMap[$stok->SiraNo] ?? 0;
        
        $iskontolu_fiyat = $fiyati - ($fiyati * $iskonto_orani) / 100;
        $stok['SatisFiyati'] = ceil($iskontolu_fiyat);
        $stok['file_path'] = asset($stok->file_path);
        return $stok;
   });

   return response()->json($urunler);
}

优化后接口响应耗时通常可以降到100ms以内。


可选进阶优化
  • 索引优化:给Stok表的StokMiktari字段加普通索引,给User表的bayikodu字段加唯一索引,给用户折扣关联表的user_id、grup_kodu字段加联合索引,进一步降低单条SQL的执行耗时
  • 缓存配置:如果商品数据、用户折扣规则更新频率低,可以给接口加10-60秒的请求缓存,相同用户重复请求直接返回缓存结果,完全跳过查库计算逻辑
  • 字段裁剪:严格控制接口返回字段,不要返回商品详情、内部备注等列表渲染用不到的字段,减少JSON序列化和网络传输开销
  • 路径预处理:如果静态资源域名固定,可以直接在商品表存储完整访问路径,或者在模型层做好访问器自动拼接路径,减少接口层重复计算

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 05:39:18