laravel 主表和从表一对一,从表是要多个选取最新的一条,性能优化

2025年6月10日10:35:15

Driver 模型关联方法
    // 关联司机定位表
    public function driverLocation()
    {
        return $this->hasOne(\App\Models\DriverLocation::class, 'driver_id', 'id');
    }

// 关联方法

 $query = Driver::with([
            // 第一种常规写法
            'driverLocation' => function ($query) {
                $query->orderBy('id', 'desc');
            },
            // 第二种 减少结果集排序
            'driverLocation' => function ($query) {
                $query->orderBy('id', 'desc')->limit(1);
            },
            // 使用max子查询
            'driverLocation' => function ($query) {
                $query->whereIn('id', function ($sub) {
                    $sub->selectRaw('MAX(id)')
                        ->from('driver_locations')
                        ->groupBy('driver_id'); // 分组获取每组最大 id
                });
            }
        ]);
        
窗口函数 sql,其实性能也不好
SELECT *
FROM (
    SELECT *,
        ROW_NUMBER() OVER (
            PARTITION BY driver_id 
            ORDER BY 
                id DESC  -- 时间相同时取最大id(最新插入)
        ) AS rn
    FROM driver_location
) AS ranked
WHERE rn = 1;

Driver关联 driverLocation表,driverLocation是多条,关联的是最新的一条数据的需要,但是 driverLocation有几百万条数据,所以 按照平常的写法导致性能很差

第一种,是最常见的倒序排序,在关联最新的一条,但是结果集需要排序,如果 driverLocation的数据量很大,就会很慢

第二种,是第一种的优化结果,这样就不需要吧结果集在排序,但是mysql本身会使用 ->orderBy('id', 'desc') 排序性能一般,如果 driverLocation的数据量很大,性能就一般,适合关联数据集不多的,性能就没问题

第三种,直接通过MAX函数,获取最新一条

推荐方式,这种需要确定关联最新一条的数据,如果数据量不大,那么 $query->orderBy('id', 'desc')->limit(1); 这种是最好的
如果数据量大,需要关联最新的一条,不论什么方式,性能都不太好,建议在redis存一下,这样直接去redis里取值,性能是最好的
比如这里的场景,司机定位信息,一般需要比较及时的,一般缓存一下 司机id作为key,gps信息json,作为value,有效时间为1小时

posted on 2025-06-10 10:45  zh7314  阅读(23)  评论(0)    收藏  举报