索引可用但未被SQL命中 有索引环境执行慢于无索引环境排查
问题根因
两个环境同SQL性能差异巨大,核心是MySQL优化器选择的执行路径不同:
- 无索引环境只能走全表扫描:按数据物理存储顺序逐行匹配
posted_to_gfj=1、posted_date<'2022-06-28'的条件,凑够LIMIT要求的8000条就立刻终止扫描,不需要做全量排序,只要符合条件的数据不是集中在表的末尾,仅需扫描少量数据即可完成,因此耗时仅2秒。 - 有索引但执行慢的环境,本质是优化器选错了执行方案:
- 现有索引为不匹配SQL逻辑的单列索引(多为
posted_to_gfj单列索引),优化器误判走该索引的成本更低:实际执行时需要先从索引中捞出所有posted_to_gfj=1的主键ID,再回表逐行查询posted_date字段做过滤、排序。回表是随机IO,开销是全表扫描顺序IO的数倍,且需要处理完所有posted_to_gfj=1的记录才能排序取前8000条,涉及数据量达几十万甚至上百万,因此耗时长达80秒。 - 若表长期未更新统计信息,优化器对符合条件的行数、索引选择成本的估算会出现严重偏差,也会导致其放弃高效路径,选择最慢的执行方式。
- 现有索引为不匹配SQL逻辑的单列索引(多为
验证方法
在慢执行的环境中,给SQL加IGNORE INDEX忽略现有不匹配的单列索引,强制走全表扫描:
UPDATE jobs_feed IGNORE INDEX(填写当前表上posted_to_gfj、posted_date相关的单列索引名) SET posted_to_gfj=3 WHERE posted_to_gfj = 1 AND posted_date < '2022-06-28' ORDER BY posted_date ASC LIMIT 8000;
如果执行耗时直接降到2秒左右,即可确认是优化器选错索引导致的性能问题。
解决方案
- 先更新表的统计信息,修正优化器的成本估算偏差:
ANALYZE TABLE jobs_feed;
- 创建完全匹配SQL逻辑的联合索引,从根源上让优化器选中最优执行路径:
ALTER TABLE jobs_feed ADD INDEX idx_gfj_posteddate(posted_to_gfj, posted_date);
该联合索引会先按posted_to_gfj过滤出值为1的记录,这部分记录在索引中已经按posted_date排序,执行时直接顺着索引取前8000条符合时间条件的记录回表更新即可,无需全量排序,走顺序IO,正常耗时可压到几百毫秒。
3. 如果更新统计信息后优化器仍选择错误索引,可在SQL中加FORCE INDEX(idx_gfj_posteddate)强制走新建的联合索引,彻底避免优化器误判。
内容的提问来源于stack exchange,提问作者Chowdary
相关产品推荐
相关产品推荐

