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

Laravel Eloquent查询优化:添加clicks_count字段提速排序查询

优化方案:新增clicks_count字段优化Laravel关联排序查询

问题根源

原查询依赖withCount('clicks')生成的动态聚合字段clicks_count进行排序,数据库无法对这个实时计算的字段使用索引,在百万级数据量下需要遍历所有匹配记录并重新计算聚合值,直接导致查询性能暴跌。


具体实现步骤

1. 给ShortUrl表新增clicks_count字段

生成迁移文件:

php artisan make:migration add_clicks_count_to_short_urls_table --table=short_urls

编辑迁移文件的up方法:

public function up()
{
    Schema::table('short_urls', function (Blueprint $table) {
        // 用无符号整数存储点击数,默认值设为0
        $table->unsignedInteger('clicks_count')->default(0);
    });
}

运行迁移生效:

php artisan migrate

2. 维护clicks_count的实时同步

由于ShortUrl和Click是一对多关联,需要在Click记录新增/删除时同步更新对应ShortUrl的点击数,推荐用模型观察者实现:

生成Click模型观察者:

php artisan make:observer ClickObserver --model=Click

编辑app/Observers/ClickObserver.php:

namespace App\Observers;

use App\Models\Click;

class ClickObserver
{
    public function created(Click $click)
    {
        // 新增点击记录时,对应短链接的点击数+1
        $click->shortUrl()->increment('clicks_count');
    }

    public function deleted(Click $click)
    {
        // 删除点击记录时,对应短链接的点击数-1
        $click->shortUrl()->decrement('clicks_count');
    }
}

在app/Providers/AppServiceProvider.php的boot方法中注册观察者:

use App\Models\Click;
use App\Observers\ClickObserver;

public function boot()
{
    Click::observe(ClickObserver::class);
}

注意:如果有批量新增/删除Click的操作,需要手动调用increment/decrement或者批量更新clicks_count,避免数据不一致。

3. 修改查询代码并添加复合索引

替换原查询,直接使用数据库字段clicks_count,无需再调用withCount:

$shorturls = ShortUrl::where([
    ["url_id", "=", $id->id],
    ["user_id", "=", Auth::id()]
])->orderBy("clicks_count", "desc")->paginate(10);

为了让数据库直接通过索引完成筛选和排序,添加复合索引:
生成索引迁移文件:

php artisan make:migration add_index_to_short_urls_for_user_url_clicks --table=short_urls

编辑迁移文件:

public function up()
{
    Schema::table('short_urls', function (Blueprint $table) {
        // 复合索引匹配查询条件+排序规则,最大化查询效率
        $table->index(['user_id', 'url_id', 'clicks_count']);
    });
}

运行迁移:

php artisan migrate

4. 初始化历史数据的clicks_count

对于已存在的ShortUrl记录,需要批量计算并更新clicks_count,推荐用原生SQL执行,效率远高于ORM循环:

UPDATE short_urls su
JOIN (
    SELECT short_url_id, COUNT(*) as count
    FROM clicks
    GROUP BY short_url_id
) c ON su.id = c.short_url_id
SET su.clicks_count = c.count;

可以通过Laravel的DB::statement()执行,或者直接在数据库客户端运行。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 10:27:24