MySQL未选用正确索引问题咨询:含sent条件时无索引使用
为什么MySQL在添加
sent=1条件后不使用Index02? 这是个非常典型的查询优化器决策问题,我来拆解下背后的核心逻辑:
根本原因:优化器的成本权衡机制
MySQL的查询优化器会基于表的统计信息和执行成本估算来选择最优执行计划,这里主要有两个关键影响因素:
1. 结果集占比过高,索引选择性不足
当你的查询加上sent=1后,如果满足该条件的行数占表总行数的比例很高(通常超过30%,不同版本阈值略有差异),优化器会判定:走Index02先筛选出id_user=xxx and hour=x and created_at=curdate()的行,再回表校验sent=1的成本,比直接全表扫描更高。
索引的核心价值是快速缩小结果集,如果结果集本身就占了表的大部分数据,索引带来的过滤收益完全抵不上回表的IO开销,优化器自然会选择更高效的全表扫描。
2. 索引不覆盖查询,需要额外回表
你的Index02是(id_user,hour,created_at)复合索引,里面并不包含sent字段。这意味着即使优化器走了Index02,找到符合前三个条件的行后,还得通过主键id回表到主表中读取sent字段的值来判断是否满足条件。这种"索引查找+回表"的操作,当结果集较大时会产生大量随机IO,成本远高于全表扫描的顺序IO。
可行的解决方案
给你几个针对性的优化方向:
- 将Index02改为覆盖索引:把
sent字段加到Index02的末尾,改成(id_user, hour, created_at, sent)。这样查询需要的所有字段都包含在索引里,不需要回表,优化器会更倾向于选择这个索引,同时还能直接通过索引过滤sent=1的条件。 - 更新表的统计信息:执行
ANALYZE TABLE tb_msgs;让MySQL重新收集表的统计数据。如果之前的统计信息过时,优化器可能错误判断了sent=1的结果集大小,更新后能帮助它做出更准确的决策。 - 谨慎使用强制索引:如果你确定走Index02的性能更好,可以在查询中指定强制索引,比如:
注意:这种方式不推荐长期依赖,因为数据分布变化后,强制索引可能反而导致性能下降。SELECT * FROM tb_msgs FORCE INDEX(Index02) WHERE id_user=xxx and hour=x and created_at=curdate() and sent=1;
内容的提问来源于stack exchange,提问作者JJsws037
相关产品推荐
相关产品推荐

