Laravel雄辩地花费6-12秒返回结果

时间:2019-11-22 21:52:08

标签: mysql laravel performance laravel-5 eloquent

我有要提取的数据,但是数据库中有很多行(超过100k),因此可能需要12秒才能返回结果。关于如何提高性能的任何想法?

  $completedActivities = Activity::where('executed_at', '>', Carbon::now()->subDays(14))
        ->where('pending', false)
        ->where('user_id', auth()->user()->id)
        ->orderby('executed_at', 'desc')
        ->limit(100)->get();

表结构:

Schema::create('activities', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->unsignedInteger('user_id');
            $table->string('type');
            $table->boolean('pending')->default(true);
            $table->dateTime('pending_until')->nullable();
            $table->dateTime('executed_at')->nullable();
            $table->longText('execute')->nullable();
            $table->softDeletes();
            $table->timestamps();
        });

我尝试只提取最近14天中的项目(以减少从db中提取所有活动),并尝试限制为100个。还有其他想法吗?重构吗?

2 个答案:

答案 0 :(得分:3)

基于EXPLAIN优化SQL:

尝试使用->toSql()获取原始SQL。

Activity::where('executed_at', '>', Carbon::now()->subDays(14))->where('pending', false)->where('user_id', auth()->user()->id)->orderby('executed_at', 'desc')->toSql()

并使用explain查看说明计划,并将其放入您的SQL CLI。

EXPAIN SELECT *
FROM activities
WHERE user_id = 1 AND pending = false AND executed_at > '2019-11-23 00:00:00'

这将向您显示表扫描消息,包括过滤时间,所选类型,扫描行等。

索引建议:

您可以使用两种方法,这取决于您的sql优化器。

  1. 我发现您有一个user_id外键字段,并扫描了execute_at字段,看来您需要对其进行索引。像
$table->index('user_id')
$table->index('executed_at')

然后使用解释查看结果。

  1. 您有多种情况。我认为您可以尝试综合索引进行优化,例如
$table->index(['user_id', 'executed_at', 'pending'])

但是,如果您已经具有user_id索引和executed_at索引,则需要忽略上面的方法, 您需要先删除那些索引。

并建立此综合索引,然后通过explain进行尝试。

我认为它会有所改善,选择最好的一种。

PS:请小心组合索引的顺序。首先放置您的user_id,然后其他简单查询只能使用user_id索引,然后explain选择最佳索引。

请记住,默认情况下,关系数据库会忽略NULL值

限制数据计数。

带有索引的100k + SQL可以非常轻松,快速地完成。 但是,将所有结果都排除掉是一个非常糟糕的主意。

您需要限制记录数字段数,以便优化IO成本。

$completedActivities = Activity::where('executed_at', '>', Carbon::now()->subDays(14))
        ->where('pending', false)
        ->where('user_id', auth()->user()->id)
        ->orderby('executed_at', 'desc')
        # => Limit the records' count, find the balance number fit to frontend
        ->limit(30)               
        # => Limit the fields [Select the fields that you want]
        ->select('id', 'title', ...)    
        ->get();

节省空间

我认为可以优化某些领域,例如节省空间。

看来type不是一个长字符串。您可以将其限制为特定长度。

$table->string('type', 20);

希望这可以为您提供帮助。

答案 1 :(得分:0)

创建索引后,您可以使用内置的简单分页

  $completedActivities = Activity::where('executed_at', '>', Carbon::now()->subDays(14))
        ->where('pending', false)
        ->where('user_id', auth()->user()->id)
        ->orderby('executed_at', 'desc')
        ->simplePaginate(100);

简单分页将不计算结果记录,因此将使查询更有效