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

Laravel查询性能异常:小表查询慢于大表问题排查求助

未读消息查询性能异常排查问题

我开发了一款消息应用,为实现新消息检测功能,除了messages表之外额外创建了unread_message_flag_clients表,用一个布尔标识来标记用户是否有未读消息。原本觉得这个未读标识表字段更少、行数也远少于messages表,查询效率应该比直接查messages表找未读消息更高,但性能测试结果却相反——查messages表的速度更快,我搞不懂原因,希望有人帮忙排查。

测试脚本与查询代码

$start=hrtime(true);

UnreadMessageFlagClient::query()
                        ->where('client_id',50)
                        ->get();

$end=hrtime(true);
$eta=$end-$start;
$time1 = $eta/1e+6;

$start2=hrtime(true);

Message::query()
         ->where('talk_id',10)
         ->where('sent_by','p')
         ->whereNull('read_by_receiver')
         ->get();

$end2=hrtime(true);
$eta2=$end2-$start2;
$time2 = $eta2/1e+6;

echo 'Time1: '.$time1.' <br/><br/> Time2: '.$time2;

测试输出

测试结果始终显示第一个查询耗时更长,例如:

Time1: 3.794781
Time2: 1.475115

表结构

Schema::create('messages', function (Blueprint $table) {
    $table->id();
    $table->foreignId('talk_id');
    $table->text('message', 6000);
    $table->string('sent_by',1); // C代表客户,P代表专业人员
    $table->softDeletes();
    $table->timestamps();
});

Schema::create('unread_message_flag_clients', function (Blueprint $table) {
    $table->id();
    $table->foreignId('client_id');
    $table->boolean('unread_messages_flag')->nullable();
});

测试时messages表有486行数据,unread_message_flag_clients表有60行数据。


问题排查与解决方向

1. 索引缺失是核心原因

从表结构可以看到,unread_message_flag_clients表的client_id字段没有创建索引。当执行where('client_id',50)查询时,数据库会进行全表扫描(哪怕只有60行);而messages表的talk_id作为外键,Laravel的foreignId方法会隐式添加索引,加上sent_by的过滤条件,查询时会走索引,效率自然更高。

2. 查询逻辑可以优化

你当前用get()获取整个集合,但实际上只需要判断当前客户是否存在未读标识,用first()代替get()可以减少ORM对结果集的处理开销,进一步提升速度:

UnreadMessageFlagClient::query()
                        ->where('client_id',50)
                        ->first();

3. 用EXPLAIN验证执行计划

在数据库中直接执行两个查询的EXPLAIN语句,查看执行计划:

EXPLAIN SELECT * FROM unread_message_flag_clients WHERE client_id = 50;
EXPLAIN SELECT * FROM messages WHERE talk_id = 10 AND sent_by = 'p' AND read_by_receiver IS NULL;

通过结果可以直观看到第一个查询是否走了全表扫描,第二个查询是否使用了索引。

4. 补充索引的方案

修改unread_message_flag_clients表的迁移文件,给client_id添加索引:

Schema::create('unread_message_flag_clients', function (Blueprint $table) {
    $table->id();
    $table->foreignId('client_id')->index(); // 添加index()
    $table->boolean('unread_messages_flag')->nullable();
});

或者已创建表后,单独创建索引:

CREATE INDEX idx_unread_client_id ON unread_message_flag_clients(client_id);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 21:53:18