我有要提取的数据,但是数据库中有很多行(超过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个。还有其他想法吗?重构吗?
答案 0 :(得分:3)
尝试使用->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优化器。
user_id
外键字段,并扫描了execute_at字段,看来您需要对其进行索引。像$table->index('user_id')
$table->index('executed_at')
然后使用解释查看结果。
$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);
简单分页将不计算结果记录,因此将使查询更有效