联合索引必须满足最左前缀原则才有效,即查询须从索引最左列开始、连续使用等值条件;跳过中间列(如sn=?AND age=?缺name)或单独查右列(如name=?)均导致索引失效,范围查询还会截断右侧列的索引使用。

联合索引不满足最左前缀,直接失效
MySQL 的联合索引(如 INDEX idx_sn_name_age (sn, name, age))不是“多个字段随便查都行”,而是严格按定义顺序组织 B+ 树。只有从最左边字段开始、连续的等值条件才能命中索引。
常见错误现象:WHERE name = '张三' AND age = 25 完全用不上 sn+name+age 索引;WHERE sn = 'S001' AND age = 25 也只用到 sn,age 被跳过——因为中间缺了 name,索引链断了。
- ✅ 正确用法:
sn = ?、sn = ? AND name = ?、sn = ? AND name = ? AND age = ? - ❌ 错误写法:
name = ?、age = ?、sn = ? AND age = ?(跳过name) - ⚠️ 注意:范围查询(
>、、BETWEEN)会截断右侧所有列的索引使用,比如sn = ? AND name > 'a' AND age = 25中,age就不会走索引
LIKE 模糊查询写错位置,索引当场消失
索引只对「前缀确定」的模糊匹配有效,因为 B+ 树是按字典序排序的,MySQL 能快速定位 '张%' 对应的起始页,但对 '%张' 或 '%张%',只能扫完整个索引甚至回表全扫。
真实场景中,搜索框默认带「包含」语义,但后端若直接拼成 LIKE '%${keyword}%',哪怕有 name 索引也白搭。
- ✅ 可走索引:
name LIKE '张%'、name >= '张' AND name (手动模拟前缀) - ❌ 不走索引:
name LIKE '%张'、name LIKE '%张%'、LEFT(name, 2) = '张'(函数导致失效) - ? 补救思路:高频「含某字」搜索可考虑全文索引(
FULLTEXT)或引入 Elasticsearch,别硬扛
对索引列做计算、函数或隐式类型转换
只要 MySQL 在执行时需要先处理字段再比对,索引就大概率失效——因为索引存的是原始值,不是函数结果或转换后值。
典型例子:WHERE age + 1 = 26、WHERE UPPER(name) = 'LISA'、WHERE name = 123(name 是 VARCHAR,传入数字触发隐式转换)。
- ✅ 安全写法:
age = 25、name = '123'(字符串加引号)、DATE(create_time) = '2026-03-19'改为create_time >= '2026-03-19' AND create_time - ❌ 高危操作:
WHERE YEAR(create_time) = 2026、WHERE CAST(age AS CHAR) = '25'、WHERE name + '' = 'abc' - ? 验证方法:用
EXPLAIN看key和key_len是否非空,type是否为ref/range,而非ALL
OR 条件里混用无索引字段,整条路堵死
MySQL 一般不会为 OR 各自走不同索引再合并结果(除非开启 index_merge 且优化器判定划算)。更常见的是:只要 OR 中有一个分支没索引,整个 WHERE 就可能放弃索引,退化为全表扫描。
比如 WHERE status = 'active' OR user_id = 123,如果 status 没索引,即使 user_id 有索引,也可能不走。
- ✅ 替代方案:
UNION ALL拆开(确保各子句能独立走索引):(SELECT ... WHERE status = 'active') UNION ALL (SELECT ... WHERE user_id = 123) - ✅ 或补索引:
status加单列索引,或建覆盖索引(status, user_id) - ⚠️ 注意:
IN通常比OR更友好,但IN列表过大(如上千项)也会让优化器放弃索引
最左前缀不是教条,而是 B+ 树物理结构决定的硬约束;失效往往不是 MySQL “不聪明”,而是我们写的 SQL 超出了索引能高效服务的边界。查慢查询时,第一眼盯 EXPLAIN 里的 key 和 Extra(尤其是 Using filesort、Using temporary),比猜原因快得多。











