优化MySQL慢查询:40万行数据查询耗时50秒求改进方案
嘿,咱们先拆解你的查询问题,一步步优化,再聊聊执行计划的门道~
一、原查询的核心问题
先揪出几个拖慢速度的关键点:
- 逻辑运算符优先级坑:
AND比OR优先级高,原查询里的pfiles.media_type = 'PRINT' AND clients.market = 17 OR client_markets.market_id = 17 OR client_type = 'INTERNATIONAL'会被MySQL解析成(pfiles.media_type = 'PRINT' AND clients.market = 17) OR client_markets.market_id = 17 OR client_type = 'INTERNATIONAL'——这意味着大量不符合media_type='PRINT'的行也会被拉出来,直接导致数据量爆炸。 - JOIN导致的行数膨胀:
client_markets的JOIN可能让单条client数据生成多条重复行,后续GROUP BY又要花时间去重,纯纯的资源浪费。 - GROUP BY/ORDER BY无索引支撑:如果
pfiles.pfile_id是主键,GROUP BY其实是多余的;而ORDER BY pfile_date如果没有索引,会触发文件排序,40万行的话这一步就会占掉大部分时间。 - 缺失关键索引:关联字段(比如
pfile_print_media.pfile_id)、过滤字段(pfiles.media_type、clients.market)都没索引的话,全表扫描跑起来肯定慢。
二、优化后的查询写法
先修正逻辑,用EXISTS替代冗余JOIN,提前过滤数据:
SELECT ppm.pfile_print_media_id, m.market_name, c.full_name, pf.image, pf.pfile_date FROM pfiles pf -- 先过滤PRINT类型,减少后续JOIN的数据量 WHERE pf.media_type = 'PRINT' -- 用EXISTS替代JOIN,避免行数膨胀 AND EXISTS ( SELECT 1 FROM clients c WHERE c.client_id = pf.client_id AND ( c.market = 17 OR c.client_type = 'INTERNATIONAL' OR EXISTS ( SELECT 1 FROM client_markets cm WHERE cm.client_id = c.client_id AND cm.market_id = 17 ) ) ) LEFT JOIN pfile_print_media ppm ON ppm.pfile_id = pf.pfile_id LEFT JOIN publications pub ON pub.publication_id = ppm.publication_id LEFT JOIN clients c ON c.client_id = pf.client_id LEFT JOIN market m ON m.market_id = c.market -- 如果pfile_id是pfiles的主键,直接去掉GROUP BY!因为主键唯一,不会有重复行 ORDER BY pf.pfile_date DESC LIMIT 4;
优化点说明:
- 把
pfiles作为主表,先过滤media_type='PRINT',从源头减少数据量。 - 用
EXISTS替代client_markets的JOIN,找到匹配项就停止扫描,避免生成重复行。 - 修正了WHERE子句的逻辑优先级,确保
media_type='PRINT'是所有条件的前提。
三、必须加的关键索引
这些索引能直接把查询速度拉上去:
pfiles(media_type, pfile_date, client_id, image):覆盖过滤、排序和SELECT需要的字段,彻底避免回表。clients(client_id, market, client_type):加速EXISTS子查询的条件匹配。client_markets(client_id, market_id):快速定位符合条件的客户市场关联。- 关联字段索引:
pfile_print_media(pfile_id)、publications(publication_id)、market(market_id),消除JOIN时的全表扫描。
四、执行计划输出详解
用EXPLAIN跑你的原查询,重点看这几个字段:
- type:最核心的性能指标,从快到慢排序:
const>eq_ref>ref>range>ALL。如果出现ALL(全表扫描),说明对应表没加索引。 - key:MySQL实际用到的索引,如果是
NULL,说明没用到索引,这就是慢的根源。 - Extra:
Using filesort:表示MySQL要额外做排序,通常是ORDER BY的列没索引,必须干掉。Using temporary:需要创建临时表存中间结果,比如GROUP BY没索引时会出现,非常耗时。Using index:使用了覆盖索引,不需要回表查数据,这是我们想要的最优状态。Using where:存储引擎取出行后再过滤,说明过滤条件没用到索引。
- rows:MySQL预估要扫描的行数,这个数字越小越好,加索引后会大幅下降。
举个例子:如果原查询的pfiles表的type是ALL,Extra有Using filesort和Using temporary,那就是全表扫描+文件排序+临时表,50秒完全合理;加完索引后,type会变成ref或range,Extra会变成Using index,速度能秒级响应。
内容的提问来源于stack exchange,提问作者Uday B.
相关产品推荐
相关产品推荐

