联合索引不走索引的根本原因是b+树按最左列全局排序、右列仅局部有序,跳过最左列(如where b=2)或中间列(如where a=1 and c=3)导致无法定位数据区间,优化器只能全表扫描。

联合索引不走 EXPLAIN 的真实原因
不是 SQL 写得不够“漂亮”,而是 B+ 树物理结构决定了它只能从最左列开始连续匹配。比如有索引 INDEX idx_name_age_pos(name, age, pos),WHERE age = 22 AND pos = 'dev' 这条语句一定不会用上这个索引——因为树的根节点和非叶节点只按 name 排序,压根没有 age 的全局顺序信息,MySQL 没法跳过 name 直接定位 age 区间。
常见错误现象包括:type 字段显示 ALL(全表扫描),key 为 NULL,rows 值接近表总行数。这些不是偶然,是索引根本没被选中。
- MySQL 查询优化器会重排
WHERE条件顺序,所以WHERE pos='x' AND name='y' AND age=22和定义顺序无关,照样能用索引 - 但只要条件里缺了最左列(
name),或中间跳过了某列(如只有name和pos),后续列就失效 - 范围查询(
>、、<code>BETWEEN、LIKE前缀不固定)会让其右侧所有列无法参与索引查找
WHERE a=1 AND c=3 为什么只用上 a 列
联合索引 (a, b, c) 的叶子节点是严格按 a→b→c 三级排序的。当查询只含 a=1 和 c=3,MySQL 可以快速定位所有 a=1 的数据块,但这些块内 c 是无序的(因为 b 值不同导致 c 分散),没法再用索引二分或范围跳转找 c=3,只能对每个 a=1 的记录逐条判断 c 值。
这叫“部分索引生效”:执行计划里 key_len 显示只用了前 1 列长度,Extra 中可能出现 Using where(表示回表后还要在服务器层过滤)。
-
WHERE a=1 AND b>2 AND c=3:c列完全失效,哪怕值精确匹配 -
WHERE a=1 AND b LIKE 'abc%':b是前缀匹配,仍有序,c后续列依然失效 -
WHERE a=1 AND b IN (1,2,3) AND c=3:IN算等值列表,c可用(但注意 MySQL 5.7+ 才稳定支持)
怎么设计才让最左前缀真正“有用”
不是字段堆得越多越好,关键看查询模式是否高频覆盖前缀子集。比如业务里 80% 查询都带 status 和 created_at,那 (status, created_at, user_id) 就比反过来更合理——因为 status 区分度高且常作为第一筛选条件。
容易被忽略的点:ORDER BY 和 GROUP BY 字段如果和索引前缀一致,也能避免额外排序;而 SELECT * 在二级索引下必然触发回表,若只查索引列本身(覆盖索引),性能会显著提升。
- 高区分度字段放左边(如
user_id、order_no),低区分度(如status、is_deleted)靠后 - 等值查询字段优先于范围查询字段,把
WHERE a = ?放前面,AND b > ?放后面 - 如果经常
ORDER BY a, b,且WHERE也含a,那(a, b)就比单独建(a)+(b)更高效
LIKE '%xx' 为什么直接放弃索引
B+ 树依赖“前缀有序性”做快速跳转。LIKE 'xx%' 可以定位到 xx 开头的所有键值区间,但 LIKE '%xx' 要求匹配任意前缀+固定后缀,B+ 树无法从根节点推导出任何起始位置,只能扫完整个索引叶子链表,效率不比全表扫描高多少,优化器干脆弃用。
这类写法在 EXPLAIN 中表现为 key 为 NULL,type 为 index 或 ALL,Extra 里还可能带 Using filesort 或 Using temporary。
- 想模糊搜后缀?考虑倒序存储 +
LIKE 'xx%',例如存REVERSE(email)再建索引 - 全文检索场景别硬扛,用
FULLTEXT索引或 Elasticsearch -
LIKE 'xxx%'是安全的,但要注意字符集和排序规则影响前缀截断(如utf8mb4_unicode_ci下大小写不敏感可能隐式扩大范围)
OR 条件混用、隐式类型转换这些地方,EXPLAIN 一看便知,但很多人跳过这步直接上线。











