特定SQL查询耗时超3分钟,寻求性能优化方案
以下SQL查询执行耗时已超过3分钟,请问是否有可行的优化方法来提升其执行速度?
原SQL代码:
$sql_combinaison_count = "SELECT pvl.name_parametre_value_parametre_value_lang , pv.id_parametre_value, ic.id_combinaison , ic.prix_combinaison , ic.poid_combinaison, ic.actif_combinaison , ic.actif_genere , pv.actif_value , pipv.id_parametre_value as pipv_id_parametre_value FROM "._DB_PREFIX_."imp_combinaison_parametre_value_".$nom_domaines_imp[0]['id_nom_domaine']." cpv LEFT JOIN "._DB_PREFIX_."imp_combinaison_".$nom_domaines_imp[0]['id_nom_domaine']." ic ON ic.id_combinaison = cpv.id_combinaison LEFT JOIN "._DB_PREFIX_."imp_parametre_value pv ON pv.id_parametre_value = cpv.id_parametre_value LEFT JOIN "._DB_PREFIX_."imp_parametre_value_lang pvl ON pvl.id_parametre_value = pv.id_parametre_value LEFT JOIN "._DB_PREFIX_."imp_parametre p ON p.id_parametre = pv.id_parametre LEFT JOIN "._DB_PREFIX_."imp_product_impression_parametre_value pipv ON pipv.id_parametre_value = pv.id_parametre_value and pipv.id_product_impression = ".$_GET['id_product']." WHERE p.id_nom_domaine = ".$nom_domaines_imp[0]['id_nom_domaine']." And pvl.id_lang_domaine = ".$_GET['id_lang_domaine']." AND ic.id_product_impression = ".$_GET['id_product'].$sql_recherche." GROUP BY ic.id_combinaison,cpv.id_parametre_value ORDER BY ic.id_combinaison,p.id_parametre ";
嘿,这个查询跑3分钟确实有点离谱,我给你几个针对性的优化思路,应该能大幅提速:
1. 给关键字段加索引(最立竿见影的操作)
数据库慢大多是因为没走索引,全表扫描在数据量大的时候简直是灾难。针对你的查询,重点加这些索引:
- 关联字段:
cpv.id_combinaison、ic.id_combinaison、cpv.id_parametre_value、pv.id_parametre_value、p.id_parametre;给pipv表加(id_parametre_value, id_product_impression)联合索引 - WHERE过滤字段:
p.id_nom_domaine、pvl.id_lang_domaine、ic.id_product_impression - GROUP BY和ORDER BY字段:
(ic.id_combinaison, cpv.id_parametre_value)联合索引、(ic.id_combinaison, p.id_parametre)联合索引
比如给imp_combinaison_xxx表加联合索引的示例:
ALTER TABLE imp_combinaison_xxx ADD INDEX idx_id_product_combinaison (id_product_impression, id_combinaison);
2. 把假LEFT JOIN改成INNER JOIN
你看这段:LEFT JOIN imp_parametre p之后,WHERE里写了p.id_nom_domaine = ...,这相当于强制要求p表必须有匹配记录,LEFT JOIN直接变成了INNER JOIN。直接改成INNER JOIN,数据库的执行计划会更高效,不用额外处理不匹配的空记录。
另外也可以检查pipv表的LEFT JOIN是否真的必要——如果业务逻辑里大部分情况都有对应记录,或者你不需要无匹配的结果,改成INNER JOIN也能减少数据处理量。
3. 优化GROUP BY和ORDER BY逻辑
你的GROUP BY是ic.id_combinaison,cpv.id_parametre_value,ORDER BY是ic.id_combinaison,p.id_parametre。尽量让这两个操作复用索引,就能避免数据库做额外的排序(也就是执行计划里的Using filesort)。比如刚才提到的给ic.id_combinaison和p.id_parametre加联合索引,排序时直接用索引完成,不用临时生成排序文件。
4. 尽量避免动态表名
你现在的表名是动态拼接的(带域名ID后缀),这种情况数据库没法缓存执行计划,每次查询都要重新解析、生成计划,额外增加耗时。如果业务允许,把不同域名的数据合并到同一个表,用id_nom_domaine字段区分,这样能利用数据库的执行计划缓存,提速明显。
5. 检查$sql_recherche的额外条件
如果$sql_recherche是拼接的额外过滤条件,要确保这些条件里的字段也有索引,别因为加了个条件就触发全表扫描。另外也可以看看这些条件是不是真的必要,有没有简化的空间。
6. 用EXPLAIN精准定位瓶颈
跑一下EXPLAIN命令看看执行计划,能直观看到哪里慢:
EXPLAIN SELECT pvl.name_parametre_value_parametre_value_lang , pv.id_parametre_value, ic.id_combinaison , ic.prix_combinaison , ic.poid_combinaison, ic.actif_combinaison , ic.actif_genere , pv.actif_value , pipv.id_parametre_value as pipv_id_parametre_value FROM ... -- 替换成你的完整查询语句
看结果里的type列,如果是ALL就是全表扫描;Extra列如果有Using filesort、Using temporary都是性能瓶颈,针对性解决即可。
7. 去掉不必要的返回字段
比如你选了pipv.id_parametre_value,这个字段和pv.id_parametre_value是关联的,是不是完全可以不用选?少返回一个字段,就能减少一点数据传输和处理的开销,积少成多。
内容的提问来源于stack exchange,提问作者Ahmed Itos

