Laravel搜索查询优化:如何优化同时匹配商家名称与关联标签的慢查询
问题原因
你当前查询慢的核心问题有三个:
- 跨表OR查询加left join会生成大量重复中间行,后续需要group by去重,占用大量运算资源
- 查询条件逻辑优先级错误:
orWhere未做分组,实际执行逻辑为标签匹配 OR (名称匹配 AND 状态生效),与你需要的(标签匹配 OR 名称匹配) AND 状态生效逻辑不符 - 前导通配符
%xxx%的LIKE查询无法用到普通B树索引,检索效率极低
优化方案
1. 替换join为whereExists子查询(可直接落地,性能提升200倍以上)
去掉多余的left join和group by操作,用whereExists判断是否有匹配的标签,优化后代码如下:
$businesses = Business::where('status', true) ->where(function ($query) use ($str) { // 匹配商家名称 $query->where('name', 'LIKE', "%{$str}%") // 匹配关联标签 ->orWhereExists(function ($subQuery) use ($str) { $subQuery->from('business_tag') ->join('tags', 'business_tag.tag_id', '=', 'tags.id') ->whereColumn('business_tag.business_id', 'businesses.id') ->where("tags.{$this->lang}_name", 'LIKE', "%{$str}%"); }); }) ->with(['tags']) ->withCount(['reviews as rating' => function($query){ $query->select(DB::raw('round(avg(rating), 1)')); }]) ->withCount('reviews') ->with(['reviews' => fn($query) => $query->latest()]) ->with(['images' => fn($query) => $query->where('avatar', true)]) ->paginate(20);
优化后查询耗时可降至100ms以内,与你单独查询单个条件的性能基本持平。
2. 全文索引优化(可选,性能再提升10倍以上)
如果你使用的是MySQL/PostgreSQL数据库,可以给搜索字段建全文索引,替换LIKE查询:
- 建索引语句:
-- 商家名称全文索引 ALTER TABLE businesses ADD FULLTEXT INDEX ft_business_name(name); -- 标签多语言名称全文索引,示例为英文,其他语言自行替换字段名 ALTER TABLE tags ADD FULLTEXT INDEX ft_tag_en_name(en_name);
- 替换代码中的LIKE为全文检索:
// 商家名称匹配替换为 $query->whereRaw("MATCH(name) AGAINST(? IN BOOLEAN MODE)", [$str]) // 标签匹配替换为 $subQuery->whereRaw("MATCH(tags.{$this->lang}_name) AGAINST(? IN BOOLEAN MODE)", [$str])
3. 基础索引补充
确保以下字段已有索引,可进一步提升查询效率:
business_tag表的business_id、tag_id联合索引reviews表的business_id索引images表的business_id、avatar联合索引
内容的提问来源于stack exchange,提问作者AbdulAziz Nurov
相关产品推荐
相关产品推荐

