最左前缀原则失效:联合索引中跳过最左列(如idx_name_age_city中仅查age或city),导致无法定位B+树起始页,触发全表扫描;范围查询后右侧字段失效、隐式类型转换、LIKE前缀模糊等亦会破坏索引有序性。

WHERE条件跳过联合索引最左列,直接查中间或末尾字段
这是最常见也最容易被忽略的失效点。联合索引 idx_name_age_city(name, age, city) 的 B+ 树是按 name → age → city 逐级排序的,就像电话簿必须先选省、再选市、最后找人。一旦 WHERE 中没出现 name,哪怕写了 age = 25 或 city = 'Shanghai',MySQL 就无法定位起始页,只能全表扫描。
- ❌ 失效 SQL:
SELECT * FROM users WHERE age = 25 AND city = 'Beijing' - ✅ 修复方式一(加最左列):
SELECT * FROM users WHERE name = 'Li' AND age = 25 AND city = 'Beijing' - ✅ 修复方式二(重建索引):如果业务确实高频查
age,改用idx_age_name_city(age, name, city),但需评估写入开销和现有查询是否受影响 - ⚠️ 注意:优化器可能对
WHERE age = 25 AND name = 'Li'做自动重排,但不保证稳定;显式按索引顺序写更可靠
范围查询后右侧字段无法走索引
联合索引中,等值条件(=、IN)可连续命中多列,但一旦出现范围条件(>、、BETWEEN、LIKE 'abc%'),其右侧所有列就“断连”了——B+ 树叶子节点里,范围之后的数据不再有序,无法继续二分查找。
- ❌ 失效场景:
INDEX idx_a_b_c(a, b, c)下执行WHERE a = 1 AND b > 10 AND c = 'x'→c不走索引(key_len显示只用了前两列) - ✅ 调整策略:把高频等值字段放左边,范围字段尽量靠右;如常查
a = ? AND c = ?,且b多为范围,则建idx_a_c_b(a, c, b) - ? 验证方法:用
EXPLAIN FORMAT=TRADITIONAL看key_len值是否匹配预期列长度(如VARCHAR(50)utf8mb4 约占 202 字节,INT 占 4 字节)
隐式类型转换让最左列“形同虚设”
即使 WHERE 写了最左列,只要发生隐式转换,索引就立即失效。典型如字段是 INT,但传入字符串 '123';或字段是 VARCHAR,却用数字 123 查询。MySQL 会悄悄给索引列加 CAST(),等价于在列上用了函数。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
- ❌ 失效示例:
SELECT * FROM orders WHERE user_id = '10086'(user_id是INT)→type=ALL - ✅ 检查手段:
SHOW CREATE TABLE orders确认字段类型,再比对应用层传参类型(如 MyBatis 的@Param("uid") String uid就危险) - ✅ 修复动作:统一类型,或强制改写为
WHERE user_id = CAST('10086' AS SIGNED)(不推荐,性能仍差) - ⚠️ 连表 JOIN 时同理:
orders.user_id(VARCHAR)JOINusers.id(INT)也会触发转换,必须统一字段类型
LIKE 前缀模糊破坏最左连续性
联合索引支持 LIKE 'abc%',但不支持 LIKE '%abc' 或 LIKE '%abc%'。更隐蔽的是:当 LIKE 用于联合索引中间列时,它会让右侧列全部失效,哪怕左侧是等值条件。
- ❌ 失效组合:
INDEX idx_status_ctime(status, create_time)下WHERE status = 1 AND create_time LIKE '%2025-05%'→create_time索引完全不用,status也白搭 - ✅ 正确写法:
WHERE status = 1 AND create_time >= '2025-05-01' AND create_time - ? 补充:若真要前后模糊查,别硬扛,换
FULLTEXT+MATCH AGAINST,或引入 Elasticsearch
实际排查时,EXPLAIN 的 key 和 key_len 比 type 更关键;很多“看似走了索引”的查询,其实只用了一半,后半段仍在回表或 filesort —— 这类半失效状态,比全表扫描更难察觉。










