复合索引不能跳过最左列直接查name或age,因MySQL联合索引基于B+树按定义顺序严格排序,必须从最左列开始连续匹配才能定位数据;跳过sn则无法建立有序子集,导致全表扫描或仅部分索引生效。

复合索引为什么不能跳过最左列直接查 name 或 age
因为 MySQL 的联合索引底层是 B+ 树,数据按索引定义顺序严格排序:先排 sn,sn 相同再排 name,name 再相同才排 age。这就像电话簿——只按“姓”排好序,你却想直接翻到所有叫“三”的人,它根本没给你建“名”这一层的全局目录。
常见错误现象:
-
EXPLAIN显示type: ALL或type: index(全索引扫描),而非ref/range - 查询
WHERE name = '张三'时,哪怕name在联合索引第二位,也完全不走该索引
关键点:
- 索引
INDEX idx_sn_name_age (sn, name, age)实际只构建了三条有效路径:(sn)、(sn, name)、(sn, name, age) -
WHERE sn = ? AND age = ?会用上sn,但age条件无法利用索引排序或快速定位——中间的name被跳过,B+ 树无法保证age在sn下是有序的
WHERE sn = ? AND age > ? 为什么 age 后面的索引列失效
范围查询(>、、BETWEEN、LIKE 'abc%')会让 MySQL 停止向右“下推”索引匹配。一旦在某列用了范围条件,该列右侧所有列都失去索引加速能力。
比如索引是 (sn, name, age):
-
WHERE sn = 'A001' AND name = '李四' AND age > 25→sn和name可等值精确定位,age走范围扫描,没问题 -
WHERE sn = 'A001' AND age > 25 AND name = '李四'→ 语法合法,但name实际无法用索引过滤,因为age > 25已打断连续性,MySQL 不会在乱序的age区间里再按name做二分查找
性能影响明显:原本能 ref 的变成 range,再加一列就可能退化成 index 扫描,IO 次数陡增。
为什么 WHERE name LIKE '%张' 一定不走索引
前导通配符(% 在左边)导致 MySQL 无法利用 B+ 树的有序结构做快速跳转。索引里存的是完整字符串,而 '%张' 要求匹配所有以“张”结尾的值——这些值在索引中是分散存储的,没有局部聚集性。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
对比看看哪些写法能走索引:
- ✅
name LIKE '张%'—— 可定位到“张”开头的所有块,后续按字典序连续读取 - ❌
name LIKE '%张'—— 必须扫描整个name索引分支,逐个比对结尾 - ❌
name LIKE '%张%'—— 同理,无起始锚点,无法剪枝 - ❌
LEFT(name, 2) = '张'—— 函数操作使索引列失真,MySQL 只能计算每行函数结果再比较
注意:LIKE '张%' 能用索引的前提是 name 是索引最左列,或至少是当前连续索引路径的最右一列(如 (sn, name) 中查 sn = ? AND name LIKE '张%')。
隐式类型转换和函数调用如何悄悄让索引失效
只要你在 WHERE 子句中对索引列做了任何“运行时加工”,MySQL 就无法直接拿原始索引值去比对——因为索引里存的是字段原始值,不是函数结果或转换后值。
典型踩坑场景:
- 字段
phone是VARCHAR,却写WHERE phone = 13800138000→ MySQL 自动转成数字比对,触发隐式转换,索引失效 - 字段
create_time有索引,却写WHERE YEAR(create_time) = 2026→ 函数包裹,索引列被“遮住” - 字段
age是INT,却写WHERE age = '25'→ 字符串与整数比较,MySQL 可能转age为字符串,也可能转右边为整数,但无论哪种,都破坏了索引直查能力
验证方式很简单:EXPLAIN 结果中若出现 Extra: Using where 且没带 Using index,大概率就是这类问题;更直接的是看 key 列是否为空或非预期索引名。
真正容易被忽略的点是:这些失效往往不报错、不告警,查询照常返回结果,只是慢得离谱——尤其在数据量上来之后,QPS 掉一半都未必意识到是索引没生效。










