文章目录
-
- 《联合索引的最左前缀原则与失效场景》
-
- 一、前言:索引不总是生效的
- 二、联合索引的结构
- 三、最左前缀原则的核心
- 四、常见索引失效场景
-
- [1. **范围查询(>、<、BETWEEN、LIKE)中断匹配**](#1. 范围查询(>、<、BETWEEN、LIKE)中断匹配)
- [2. **模糊匹配前缀失效**](#2. 模糊匹配前缀失效)
- [3. **函数操作导致失效**](#3. 函数操作导致失效)
- [4. **隐式类型转换失效**](#4. 隐式类型转换失效)
- [5. **OR 条件混合失效**](#5. OR 条件混合失效)
- [6. **计算表达式失效**](#6. 计算表达式失效)
- [五、Explain 实战验证](#五、Explain 实战验证)
- 六、索引设计与优化建议
- 七、面试高频问题与答题模板
- 八、总结
《联合索引的最左前缀原则与失效场景》
一、前言:索引不总是生效的
大家好,我是程序员卷卷狗。
我们都知道给字段建索引能提高查询速度,
但很多时候,你明明建了索引,SQL 却依然走全表扫描。
面试官问:
"联合索引的最左前缀原则是什么?"
"为什么有时候用了 LIKE、OR、函数就失效了?"
要答好这个问题,必须先理解:
联合索引的底层结构是有序的前缀匹配树。
二、联合索引的结构
假设我们创建一个三列联合索引:
sql
CREATE INDEX idx_name_age_city ON user(name, age, city);
InnoDB 会在 B+ 树的叶子节点中存储:
(name, age, city, 主键id)
索引的排序方式如下:
先按 name 排序,
name 相同时按 age 排序,
name 和 age 都相同时按 city 排序。
可以理解为一张"有序排列表"
| name | age | city |
|---|---|---|
| Alice | 20 | Beijing |
| Alice | 22 | Shanghai |
| Bob | 19 | Beijing |
| Bob | 23 | Chengdu |
三、最左前缀原则的核心
定义:
MySQL 使用联合索引时,会从最左边的索引列开始匹配,
一旦遇到范围查询、模糊匹配或非连续字段,就停止匹配。
(1)完整匹配(命中索引)
sql
WHERE name='Alice' AND age=20 AND city='Beijing'
命中三列,索引完全使用。
(2)前缀匹配(部分命中)
sql
WHERE name='Alice' AND age=20
命中前两列,仍能使用索引。
(3)仅最左列匹配
sql
WHERE name='Alice'
命中第一列,索引可用。
(4)跳列查询(失效)
sql
WHERE age=20 AND city='Beijing'
name 被跳过,索引无法命中。
规则总结:
- 从左向右连续匹配;
- 一旦中断,后续字段无法使用。
四、常见索引失效场景
1. 范围查询(>、<、BETWEEN、LIKE)中断匹配
sql
WHERE name='Alice' AND age>20 AND city='Beijing';
- name 精确匹配 ;
- age 为范围匹配(>20);
- city 失效。
范围条件之后的字段不再使用索引。
2. 模糊匹配前缀失效
sql
WHERE name LIKE '%lice';
无法利用索引(前缀未知)。
sql
WHERE name LIKE 'A%';
能利用索引(前缀已知)。
3. 函数操作导致失效
sql
WHERE LEFT(name,3)='Ali';
索引失效,因为函数作用在列上。
改写方式:
sql
WHERE name LIKE 'Ali%';
4. 隐式类型转换失效
sql
WHERE age = '20';
若 age 为 INT,而 '20' 为字符串,会发生隐式转换:
sql
CAST(age AS CHAR) = '20';
索引失效。
改写:
sql
WHERE age = 20;
5. OR 条件混合失效
sql
WHERE name='Alice' OR city='Beijing';
若 city 未建立独立索引,则整体失效。
改写:
- 为 city 单独建索引;
- 或用
UNION ALL拆分查询。
6. 计算表达式失效
sql
WHERE age + 1 = 20;
索引失效,因为表达式作用在列上。
改写:
sql
WHERE age = 19;
五、Explain 实战验证
假设表结构:
sql
CREATE TABLE user (
id INT PRIMARY KEY,
name VARCHAR(20),
age INT,
city VARCHAR(20),
INDEX idx_name_age_city (name, age, city)
);
| SQL | 结果 | Extra |
|---|---|---|
SELECT * FROM user WHERE name='Alice'; |
索引生效 | Using index condition |
SELECT * FROM user WHERE name='Alice' AND age>20; |
部分命中 | Using index condition |
SELECT * FROM user WHERE age=20; |
索引失效 | Using where |
SELECT * FROM user WHERE name LIKE 'A%'; |
索引生效 | Using index condition |
SELECT * FROM user WHERE name LIKE '%A'; |
失效 | Using where |
SELECT * FROM user WHERE name='Alice' AND age=20 AND city='Beijing'; |
完全命中 | Using index |
六、索引设计与优化建议
| 设计策略 | 原则说明 |
|---|---|
| ① 建联合索引时把区分度高的字段放前面 | 提升过滤率 |
| ② WHERE、ORDER BY、GROUP BY 常用字段放在前缀位置 | 利用最左原则 |
| ③ 尽量避免函数、计算、类型转换 | 防止优化器放弃索引 |
④ 模糊匹配尽量使用后缀 % 而非前缀 % |
支持前缀匹配 |
⑤ 使用 EXPLAIN 验证索引命中情况 |
判断 type、key、Extra |
七、面试高频问题与答题模板
| 问题 | 答案要点 |
|---|---|
| Q1:什么是最左前缀原则? | 联合索引按定义顺序匹配字段,从最左开始连续使用。 |
| Q2:为什么范围查询会导致后续字段失效? | 因为 B+Tree 有序性被破坏,无法继续匹配。 |
| Q3:LIKE '%xx' 能用索引吗? | 不能,前缀未知。 |
| Q4:为什么函数会导致索引失效? | 函数在列上操作,破坏原索引值。 |
| Q5:OR 条件一定导致失效吗? | 若两侧字段都有索引则不会,否则整体失效。 |
| Q6:如何验证索引是否命中? | 使用 EXPLAIN 查看 key 与 Extra 字段。 |
八、总结
联合索引的本质是一棵按列顺序排列的有序 B+ 树。
最左前缀原则,是优化器决定是否能使用索引的根规则。
一句话记住:
匹配从左开始,遇"断"即停。
掌握这一原则,你不仅能解释索引失效的原因,
还能主动在表设计和 SQL 编写中"避坑提速"。
下一篇(第 9 篇),我将写------
《索引优化策略:高效建索引的 7 条实战准则》 ,
总结从索引区分度、联合索引布局、覆盖索引设计到业务性能调优的完整策略。