使用STRAIGHT_JOIN的MySQL查询成本降低但执行耗时增加的优化求助
分析STRAIGHT_JOIN耗时增加的原因及查询优化方案
咱们先把问题拆解开,先搞懂为什么用了STRAIGHT_JOIN反而变慢,再给你针对性的优化办法。
一、STRAIGHT_JOIN耗时翻倍的原因
对比两次EXPLAIN结果,就能看出核心问题:
原查询的执行逻辑
优化器自动选择了先扫描leads_cstm表:
- 对
leads_cstm做全表扫描(696334行),但通过cust_temp_id_c = 'xxxx'过滤后,实际只处理约10%的行(≈6.9万行) - 再用这些行的
id_c关联leads表的主键(eq_ref类型,每次关联都是精准查找,单条开销极低)
STRAIGHT_JOIN的执行逻辑
你强制优化器先扫描leads表:
- 先通过
idx_del_user索引取出所有deleted=0的行(375820行,接近全表的一半) - 然后逐行去
leads_cstm表中匹配id_c并过滤cust_temp_id_c,相当于做了37万次主键查找 - 哪怕单次主键查找很快,37万次的累计IO、CPU开销,远大于原计划中6.9万次的关联操作,所以耗时直接涨到3秒
简单说:优化器原本的选择是更优的,你强制改变了表的访问顺序,导致处理的数据量暴增,开销自然上去了。
二、优化方案(解决联合索引失效+降低查询耗时)
你之前创建的(id_c, cust_temp_id_c)联合索引没生效,是因为索引列的顺序错了。咱们一步步来优化:
1. 修复leads_cstm的联合索引
查询中cust_temp_id_c = 'xxxx'是过滤条件,需要把它放在索引的前缀位置,这样优化器才能快速定位符合条件的行。同时把id_c作为第二列,避免回表查询:
-- 先删除无效的旧索引(如果存在) DROP INDEX idx_id_c_cust_temp_id ON leads_cstm; -- 创建正确的联合索引 CREATE INDEX idx_cust_temp_id_id_c ON leads_cstm(cust_temp_id_c, id_c);
这个索引的作用:
- 直接通过
cust_temp_id_c = 'xxxx'快速定位目标行,不需要全表扫描 - 索引中已经包含
id_c,可以直接用来关联leads表,不需要回表读取leads_cstm的全量数据
2. 可选:优化leads表的索引(进一步降低开销)
原查询中leads表需要过滤deleted=0并取出id,如果你的idx_leads_id_del是(id, deleted)的顺序,可以调整为(deleted, id)的联合索引:
DROP INDEX idx_leads_id_del ON leads; CREATE INDEX idx_deleted_id ON leads(deleted, id);
这样优化器关联时可以直接用这个索引过滤deleted=0并取出id,不需要回表读取leads的其他字段,进一步降低IO开销。
3. 验证优化后的执行计划
执行EXPLAIN查看优化效果,理想的执行计划应该是:
leads_cstm表的type为ref,key是新创建的idx_cust_temp_id_id_c,rows会远小于69万,Extra显示Using index(说明用了覆盖索引,不需要回表)leads表的type为eq_ref,key是PRIMARY,rows=1,Extra显示Using where
4. 可选:将LEFT JOIN改为INNER JOIN
因为你的WHERE条件中包含cust_temp_id_c = 'xxxx',这会自动过滤掉leads表中没有匹配leads_cstm的行,所以LEFT JOIN和INNER JOIN效果完全一致。改成INNER JOIN可以让优化器有更多的执行计划选择空间,可能进一步提升效率:
SELECT id FROM leads INNER JOIN leads_cstm ON leads.id = leads_cstm.id_c WHERE deleted=0 AND cust_temp_id_c = 'xxxx';
内容的提问来源于stack exchange,提问作者Sarabjeet Singh
相关产品推荐
相关产品推荐

