如何加速含全文检索的MySQL查询?附慢查询场景详情
优化查询性能的具体方案
先拆解下原查询的几个潜在性能瓶颈:
- 你用了
LEFT JOIN但WHERE里加了MATCH(product_link)条件,这会把左连接自动转换成内连接(因为左连接未匹配的行product_link是NULL,不会被MATCH命中),不如直接用INNER JOIN更清晰,还能避免数据库做无用的左连接处理。 DISTINCT需要对关联后的大结果集做排序去重,这是很耗资源的步骤。
下面是几个针对性的优化建议,亲测能大幅降低查询耗时:
1. 先过滤产品表再关联订单表(缩小结果集)
先从special_order_product中提取符合条件的唯一order_id,再用这些order_id去关联订单表,这样能把关联的数据量压缩到最小。改写后的查询如下:
SELECT so.id, so.admin_id, so.user_id, so.total_price, so.created_at, so.updated_at FROM special_order so INNER JOIN ( SELECT DISTINCT order_id FROM special_order_product WHERE MATCH(product_link) AGAINST ('trendyol' IN BOOLEAN MODE) ) sop ON so.id = sop.order_id ORDER BY so.created_at DESC LIMIT 30;
子查询先拿到所有符合关键词的订单ID,再和订单表关联,避免了直接关联后再做DISTINCT的大量计算。
2. 给订单表添加覆盖索引
你的查询需要从special_order中读取多个字段,并且按created_at排序。可以创建一个覆盖索引,让数据库不需要回表查询主数据:
-- MySQL 8.0.14+ 支持INCLUDE语法,更节省索引空间 CREATE INDEX idx_created_at_include ON special_order (created_at DESC) INCLUDE (id, admin_id, user_id, total_price, updated_at); -- 低版本MySQL可以把所有字段放入索引 CREATE INDEX idx_created_at_full ON special_order (created_at DESC, id, admin_id, user_id, total_price, updated_at);
这个索引能让数据库直接通过索引完成排序和数据读取,彻底避免对订单表主数据的扫描。
3. 验证全文索引的有效性
先确认product_link的全文索引在正常工作,用EXPLAIN查看执行计划:
EXPLAIN SELECT DISTINCT order_id FROM special_order_product WHERE MATCH(product_link) AGAINST ('trendyol' IN BOOLEAN MODE);
如果执行计划中type列显示fulltext,说明索引生效;如果没用到,可能需要检查:
- 索引是否正确创建(确认是
FULLTEXT类型) - 关键词
trendyol是否在MySQL的停用词列表里(可以调整ft_min_word_len参数,或者在配置中自定义停用词)
4. 用EXISTS替代关联(更高效的存在性判断)
如果只需要判断订单是否有符合条件的产品,EXISTS的执行逻辑会更高效——它找到匹配的行就会停止查询,不需要返回所有匹配的产品行:
SELECT so.id, so.admin_id, so.user_id, so.total_price, so.created_at, so.updated_at FROM special_order so WHERE EXISTS ( SELECT 1 FROM special_order_product sop WHERE sop.order_id = so.id AND MATCH(sop.product_link) AGAINST ('trendyol' IN BOOLEAN MODE) ) ORDER BY so.created_at DESC LIMIT 30;
这个写法和第一个优化思路类似,但在多产品对应同一订单的场景下,EXISTS的性能表现会更好。
最后记得用EXPLAIN分析每个优化后的查询,确认执行计划是否用到了我们创建的索引,是否避免了全表扫描。
内容的提问来源于stack exchange,提问作者Şöhrət Salmanov
相关产品推荐
相关产品推荐

