MySQL 8.0 优化器根据查询条件、索引统计和代价估算选择访问计划;“字段上有索引却没走”不一定是索引失效,也可能是全表扫描更便宜。先对实际 SQL 和代表性参数运行 EXPLAIN,观察 key、type、估算 rows、filtered、Extra,再结合表规模和真实执行数据判断。
OR 与 Index Merge: WHERE a=1 OR b=2 在 a、b 各有索引时,优化器可能采用 Index Merge 合并多个索引扫描结果;也可能判断合并和回表成本更高而全表扫描。若某一侧不可索引、选择性很差或表达式复杂,OR 更可能扩大扫描。不能概括为 OR 必然让索引失效。
函数与表达式: WHERE DATE(created_at)='2026-10-01' 对索引列做函数,普通索引通常难以直接构造原列范围。可改为半开区间 created_at >= '2026-10-01' AND created_at < '2026-10-02';MySQL 8.0 也可评估函数索引或生成列索引,但需匹配表达式并考虑写入成本。
隐式类型转换: 若比较两侧类型不一致,MySQL 的转换规则可能导致索引列上的比较无法使用预期范围,实际效果取决于列类型和常量类型。例如字符串列 code 应与字符串常量比较,不要随意把列转换成数值。以执行计划和数据语义验证,不能仅凭“有转换”断言一定全表扫描。
LIKE 与选择性: LIKE 'abc%' 有机会转成范围查找;LIKE '%abc' 通常无法用普通 B+ 树定位前缀。即使条件可索引,低选择性列(如只有男女两个值)也可能因需读取大量记录而不选索引。联合索引、覆盖能力和 LIMIT 都会改变成本判断。
EXPLAIN SELECT * FROM t WHERE a=1 OR b=2;EXPLAIN SELECT * FROM t WHERE created_at >= '2026-10-01' AND created_at < '2026-10-02';EXPLAIN SELECT * FROM t WHERE code = '00123';优化应从计划异常对应的实际代价入手:更新统计信息前先确认执行计划和数据分布,再考虑 SQL 改写或索引;对比改动前后的扫描行数、延迟、写入成本。EXPLAIN ANALYZE 会运行查询,谨慎用于大表或生产高峰。
追问
OR 条件一定不能用索引吗?
不是。多个分支可分别走索引时,优化器可能采用 Index Merge;结果由代价模型和数据分布决定。
LIKE 'abc%' 和 LIKE '%abc' 一样吗?
不一样。固定前缀可以形成 B+ 树范围;前导通配符通常无法定位普通索引范围。
为什么低选择性索引可能被忽略?
若索引命中后仍需读取表中大部分行,索引扫描加回表的总代价可能高于顺序扫描。