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

Laravel应对大量定位API请求,降低CPU占用的优化方案咨询

高并发定位上传场景下的CPU与内存优化方案

核心问题拆解

当前代码的updateOrCreate逻辑是性能瓶颈的核心:它会先执行SELECT查询判断记录是否存在,再执行UPDATE/INSERT操作。高并发下大量重复的查询+写操作瞬间打满CPU;同时不必要的事务、浮点坐标精确匹配的低效查询,以及请求冗余数据处理,共同导致单请求6MB的过高内存开销,在大量请求涌入时快速耗尽EC2资源,进一步加剧CPU负载(内存不足触发swap机制,CPU开销飙升)。


1. 重构数据库操作逻辑,砍掉冗余开销

优化点1:去掉不必要的事务

updateOrCreate本身是原子操作,手动开启事务只会增加数据库锁的额外开销,直接删除DB::beginTransaction()和DB::commit()。

优化点2:调整updateOrCreate的查询条件

原代码用longitude和latitude作为查询条件,浮点型字段精确匹配不仅会导致索引失效,而且每100米的坐标本身就是新值,大部分场景下SELECT查询完全是多余的:

  • 如果业务不需要坐标去重:直接改用create,彻底砍掉查询开销
  • 如果必须按精度去重:对坐标做精度截断(比如保留小数点后6位),避免精确匹配浮点值

优化后代码示例

public function savePosition(Request $request)
{
    // 方案1:无坐标去重需求时,直接插入(性能最优)
    Position::create([
        'staff_id' => $request->staff_id,
        'date' => date('Y-m-d'),
        'longitude' => $request->longitude,
        'latitude' => $request->latitude,
        'sort' => $request->sort
    ]);

    // 方案2:需要按坐标精度去重时
    $precision = 6;
    $lon = round($request->longitude, $precision);
    $lat = round($request->latitude, $precision);
    
    Position::updateOrCreate(
        [
            'staff_id' => $request->staff_id,
            'date' => date('Y-m-d'),
            DB::raw("ROUND(longitude, $precision) = $lon"),
            DB::raw("ROUND(latitude, $precision) = $lat")
        ],
        [
            'sort' => $request->sort,
            'staff_id' => $request->staff_id,
            'date' => date('Y-m-d'),
            'longitude' => $request->longitude,
            'latitude' => $request->latitude
        ]
    );

    return response()->json(['message' => 'Posición guardada correctamente']);
}

2. 批量处理请求,减少请求总量

高速行驶时(如100km/h),单用户每秒会产生2-3次请求,大量用户同时上传会导致请求量爆炸。优化思路是本地缓存+批量上传:

  • 移动端本地积累5条坐标(即500米)或间隔30秒,再一次性上传
  • 后端新增批量保存接口,用insert批量写入数据库,效率比单条操作高10-100倍

后端批量接口示例

public function saveBatchPositions(Request $request)
{
    $positionData = collect($request->positions)->map(function ($item) {
        return [
            'staff_id' => $item['staff_id'],
            'date' => date('Y-m-d'),
            'longitude' => $item['longitude'],
            'latitude' => $item['latitude'],
            'sort' => $item['sort']
        ];
    });

    Position::insert($positionData->toArray());

    return response()->json(['message' => 'Posiciones guardadas correctamente']);
}

3. 数据库层面性能优化

索引优化

给Position表创建复合索引,覆盖查询/插入的核心条件,大幅提升查询速度:

CREATE INDEX idx_position_staff_date ON positions (staff_id, date);

关闭生产环境查询日志

Laravel调试模式下会记录所有查询日志,高并发下会占用大量内存,在.env中关闭:

APP_DEBUG=false
DB_LOG_QUERIES=false

4. 服务器与部署优化

EC2实例调整

  • 升级实例规格:CPU打满说明当前实例性能不足,可升级到T3.large或M5系列,增加CPU核心数
  • 临时开启swap分区缓解内存压力(长期建议升级内存):
sudo fallocate -l 4G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile

用队列异步处理

将定位保存操作放入队列,前端请求立即返回,后台异步处理数据库操作,避免请求阻塞:

  1. 配置Redis作为队列驱动(比数据库队列效率更高)
  2. 修改代码推送任务到队列:
public function savePosition(Request $request)
{
    SavePositionJob::dispatch([
        'staff_id' => $request->staff_id,
        'date' => date('Y-m-d'),
        'longitude' => $request->longitude,
        'latitude' => $request->latitude,
        'sort' => $request->sort
    ]);

    return response()->json(['message' => 'Posición guardada correctamente']);
}
  1. 启动队列worker:
php artisan queue:work --tries=3

5. 压缩单请求内存开销

单请求6MB内存过高,可通过以下方式优化:

  • 仅获取必要的请求字段,避免加载整个Request对象的冗余数据:
$data = $request->only(['staff_id', 'longitude', 'latitude', 'sort']);
$data['date'] = date('Y-m-d');
Position::create($data);
  • 关闭Laravel的调试模式,避免加载调试相关的冗余资源

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 14:00:47