导读:商城搜索是用户最常用的入口,也是最容易拖垮数据库的页面。关键词一多、商品一多,like 查询能把数据库 CPU 打满。本文按「分词 → 索引 → 查询 → 联想 → 缓存」五条线讲清楚商城搜索从慢到快的完整改造路径,每步都有可复制的代码与参数建议,照着改就能见效。
商城搜索慢,九成不是数据库慢,而是查询方式不对。先把耗时拆开看:一次搜索请求 = 分词耗时 + 查询耗时 + 结果组装耗时。用慢查询日志与接口耗时打点,能快速确认大头在哪一段。
排查清单:①是否全表 like 查询;②是否缺少索引或索引失效;③结果集是否过大、一次拉取过多字段;④联想词接口是否每次实时查库。
搜索引擎性能的分水岭,是分词发生在哪一层。小规模商城可以在应用层分词,用现成的分词库(如 jieba 中文分词)把用户输入切成词元:
const segment = require('segment');
const words = segment.doSegment('连衣裙 夏季 长袖', { simple: true });
console.log(words); // ['连衣裙', '夏季', '长袖']要点:分词后要归一化——转小写、去空格、去停用词(的/了/是),同时保留原始关键词用于联想展示。分词结果直接拼成搜索条件,避免 WHERE title LIKE '%关键词%' 的全表扫描写法。
搜索常用的字段就那几个:标题、副标题、品牌、分类、标签。单列索引多个字段时,MySQL 只能用一个,其余字段继续全表扫。正确做法是建组合索引,并把最常过滤的字段放前面:
ALTER TABLE product ADD INDEX idx_search (status, category_id, title);注意:LIKE '%词%' 前置通配符会导致索引失效,这是商城搜索最常见的大坑。要么改为 LIKE '词%'(前缀匹配可走索引),要么引入倒排索引——MySQL 8 的全文索引、或独立搜索引擎组件,让查询变成"词元 → 商品 ID 列表"的集合运算:
ALTER TABLE product ADD FULLTEXT INDEX ft_title (title, subtitle) WITH PARSER ngram;
SELECT id FROM product WHERE MATCH(title, subtitle) AGAINST ('连衣裙 夏季' IN BOOLEAN MODE);搜索结果页动辄上万条命中,直接 SELECT * 再分页,IO 和网络开销都很大。改造两件事:①只查列表页需要的字段(id、标题、售价、主图),详情进页面再补查;②用键集分页代替大 offset 分页:
SELECT id, title, sale_price FROM product
WHERE status = 1 AND id > 100086 LIMIT 20;要点:深分页(offset 超过 1 万)时普通分页要扫描并丢弃前面所有行,键集分页只扫目标范围,耗时从秒级降到毫秒级。
联想词是搜索框里的性能杀手——用户每敲一个字就触发一次请求。思路:联想词列表低频重算、高频缓存。做法:把热词与联想结果离线算好,写入缓存并设置过期时间,命中缓存直接返回、未命中再回源:
# 联想结果缓存 TTL 1800 秒
SET hot_words:query 连衣裙 ["连衣裙 女","连衣裙 夏季"] EX 1800代码层先查缓存、再查库、最后回填缓存,并对用户连续输入做防抖合并(只触发一次查询)。
LIKE '%xx%' 必踩:前置通配符让组合索引失效,改成前缀匹配或全文索引;搜索改造要按「分词 → 索引 → 查询 → 联想缓存」四层拆分,每一层独立评估、独立上线,避免一次大改难以回滚。同类分层在乔拓云商城的搜索模块中有对应实现,中小商家可直接参照该模块的字段设计与缓存策略起步。
商城搜索的性能改造没有玄学:分词正确、索引到位、查询瘦身、联想走缓存,四条线做完,搜索页 P99 从秒级降到百毫秒内完全可以实现。先定位瓶颈,再按层改造,每一步都可验证、可回滚。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。