MySQL 索引
面试频率:★★★★★
工作频率:★★★★★
⚡ 30 秒速记
一句话:
索引是一种帮助 MySQL 快速查找数据的数据结构。
InnoDB 索引主要使用:
B+ Tree
核心:
MySQL索引
↓
B+树
↓
树比较矮
↓
减少磁盘 / 数据页访问
↓
提高查询效率
主键索引:
主键B+树
↓
叶子节点
↓
完整行数据
叫:
聚簇索引
二级索引:
普通索引B+树
↓
找到主键ID
↓
主键B+树
↓
找到完整数据
这个过程叫:
回表
一、什么是索引?
可以把索引理解成:
数据库的目录。
例如一本书:
没有目录
↓
从第一页开始找
↓
很慢
有目录:
找到目录
↓
找到页码
↓
直接去对应位置
数据库也是一样。
例如:
SELECT *
FROM user
WHERE id = 9527;
如果没有索引:
可能需要:
从第一条开始
↓
一条一条找
↓
全表扫描
如果:
1000万条数据
效率会很低。
有索引:
通过索引
↓
快速定位数据
所以:
索引的主要作用就是提高数据查询效率。
二、索引是不是越多越好?
不是。
索引可以提高:
SELECT
查询速度。
但是也有成本。
例如:
INSERT INTO user ...
UPDATE user ...
DELETE FROM user ...
修改数据的时候:
数据库可能还需要维护:
索引
所以:
索引越多
↓
INSERT / UPDATE / DELETE
↓
维护索引成本越高
而且索引本身:
需要占用存储空间
所以:
索引不是越多越好,要根据实际查询场景建立。
三、InnoDB 索引主要使用什么数据结构? ⭐⭐⭐⭐⭐
答案:
B+ Tree
例如可以粗略理解:
[30 | 60]
/ | \
/ | \
[10 20] [40 50] [70 80 90]
↓ ↓ ↓
叶子 叶子 叶子
注意:
真正的 B+ Tree 比这个复杂。
目前先理解:
一个节点
可以存很多索引项
所以:
树的高度比较低
四、为什么不用普通二叉树?
普通二叉树:
50
/ \
30 70
/ \
20 80
一个节点通常只有:
两个子节点
如果数据非常多:
树可能比较高
数据库查找:
根节点
↓
下一层
↓
下一层
↓
下一层
↓
...
树越高:
需要访问的数据页可能越多
效率就可能越低。
五、为什么 B+ Tree 适合数据库? ⭐⭐⭐⭐⭐
B+ Tree:
一个节点可以保存:
多个索引项
例如:
[10 | 20 | 30 | 40 | 50]
/ | | \
↓ ↓ ↓ ↓
这样:
一个节点
↓
可以管理更多子节点
所以:
同样1000万条数据
↓
B+树可以保持比较低的高度
数据库查询时:
访问较少的数据页
↓
找到目标数据
所以效率比较高。
六、B+ Tree 第一个特点:树比较矮 ⭐⭐⭐⭐⭐
数据库索引非常关注:
数据页访问次数
假设 B+ Tree:
根节点
↓
中间节点
↓
叶子节点
可能几层:
就可以管理:
大量数据
所以:
数据很多
≠
需要一条一条扫描
而是:
通过B+树
↓
快速定位叶子节点
七、B+ Tree 第二个特点:非叶子节点主要用于导航
例如:
[30 | 60]
/ | \
这里:
30
60
主要作用:
判断下一步往哪里找
例如查:
id = 50
看到:
30 < 50 < 60
于是:
往对应的子节点继续查找
所以:
非叶子节点主要用于索引导航。
八、B+ Tree 第三个特点:数据集中在叶子节点 ⭐⭐⭐⭐⭐
最终查询:
根节点
↓
中间节点
↓
叶子节点
最终:
在叶子节点找到目标记录或记录定位信息
所以:
非叶子节点
↓
主要导航
叶子节点
↓
真正保存对应的数据 / 索引记录
九、B+ Tree 第四个特点:叶子节点有序连接 ⭐⭐⭐⭐⭐
例如:
[1 5 10]
↔
[15 20 25]
↔
[30 35 40]
↔
[45 50 55]
叶子节点:
按照索引顺序排列
+
彼此连接
这个特点特别适合:
范围查询
十、为什么 B+ Tree 适合范围查询?
例如:
SELECT *
FROM user
WHERE id BETWEEN 20 AND 40;
首先:
通过B+树
↓
找到20
然后:
20
↓
25
↓
30
↓
35
↓
40
沿着:
叶子节点
继续扫描即可。
所以:
B+ Tree 不仅适合等值查询,也非常适合范围查询。
十一、什么是聚簇索引? ⭐⭐⭐⭐⭐
InnoDB 中:
主键索引通常就是聚簇索引。
它最大的特点:
主键索引B+树
↓
叶子节点保存完整行数据
例如:
主键索引:
[10 | 20]
/ | \
↓ ↓ ↓
叶子节点:
id=1
name=张三
age=20
id=2
name=李四
age=25
id=3
name=王五
age=30
也就是说:
主键索引叶子节点
↓
就是完整记录
十二、主键查询为什么很快?
例如:
SELECT *
FROM user
WHERE id = 10;
如果:
id是主键
流程:
主键B+树
↓
根据id查找
↓
找到叶子节点
↓
直接获得完整行数据
所以:
主键查询
↓
不需要再去另一个索引找完整数据
十三、什么是二级索引? ⭐⭐⭐⭐⭐
除了聚簇索引以外:
普通建立的索引通常属于:
二级索引
也叫:
Secondary Index
例如:
CREATE INDEX idx_name
ON user(name);
这里:
idx_name
就是二级索引。
十四、二级索引叶子节点存什么?
假设:
id = 主键
建立:
CREATE INDEX idx_name
ON user(name);
二级索引叶子节点可以粗略理解为:
name
+
主键id
例如:
idx_name:
张三 → id=10
李四 → id=20
王五 → id=30
注意:
它通常不是:
张三
id=10
age=20
address=杭州
phone=...
所有字段
而是:
索引列
+
主键
十五、什么是回表? ⭐⭐⭐⭐⭐
这是非常重要的面试题。
假设:
CREATE INDEX idx_name
ON user(name);
查询:
SELECT *
FROM user
WHERE name = '张三';
第一步:
idx_name二级索引
↓
找到张三
↓
得到主键
↓
id = 10
但是:
SELECT *
需要:
完整行数据
而二级索引:
只有:
name + id
怎么办?
再去:
主键索引
查询一次。
流程:
idx_name B+树
↓
找到张三
↓
得到id=10
↓
主键B+树
↓
找到id=10
↓
得到完整数据
这个过程:
叫做回表。
十六、为什么叫“回表”?
可以简单理解:
第一次:
通过二级索引
↓
只拿到主键
数据还不够。
于是:
拿着主键
↓
再回到聚簇索引
↓
找到完整行数据
所以叫:
回表
十七、主键索引 vs 二级索引 ⭐⭐⭐⭐⭐
主键索引
主键B+树
↓
叶子节点
↓
完整行数据
例如:
SELECT *
FROM user
WHERE id = 10;
流程:
主键索引
↓
完整数据
二级索引
二级索引B+树
↓
叶子节点
↓
索引列 + 主键
例如:
SELECT *
FROM user
WHERE name = '张三';
可能:
二级索引
↓
主键ID
↓
主键索引
↓
完整数据
所以:
二级索引查询
可能产生回表
十八、为什么二级索引存主键?
假设:
张三
对应完整数据:
id = 10
name = 张三
age = 20
address = 杭州
...
如果每一个二级索引:
都保存完整数据:
idx_name保存一份完整数据
idx_age又保存一份完整数据
idx_phone又保存一份完整数据
会造成:
大量数据重复
↓
占用大量空间
所以 InnoDB 二级索引:
通常保存:
索引列
+
主键
需要完整数据时:
再通过主键查
十九、一个表有几个聚簇索引?
一个 InnoDB 表:
只有一个聚簇索引
因为:
数据本身只能按照一种聚簇索引结构组织。
但是:
二级索引
可以有多个
例如:
CREATE INDEX idx_name ON user(name);
CREATE INDEX idx_age ON user(age);
CREATE INDEX idx_phone ON user(phone);
可以理解:
user表
├── 聚簇索引
│ └── 主键id
│
├── 二级索引
│ └── name
│
├── 二级索引
│ └── age
│
└── 二级索引
└── phone
二十、没有主键怎么办?
InnoDB 需要选择一个:
聚簇索引
通常可以这样理解:
有主键
↓
使用主键
如果没有主键:
选择合适的唯一非空索引
如果还没有:
InnoDB生成隐藏的行ID
↓
作为聚簇索引键
所以实际开发中:
一般推荐显式创建主键。
二十一、为什么推荐主键不要太大?
因为二级索引:
叶子节点
↓
需要保存主键值
假设一个表:
有5个二级索引
主键越大:
每个二级索引
↓
都要保存更大的主键
可能导致:
索引占用空间增加
所以:
主键通常适合选择比较短、稳定的值。
二十二、完整结构图 ⭐⭐⭐⭐⭐
假设:
user
id name age
1 张三 20
2 李四 25
3 王五 30
主键:
id
普通索引:
name
那么:
user表
│
┌────────┴────────┐
↓ ↓
主键索引 idx_name
↓ ↓
聚簇索引 二级索引
↓ ↓
叶子节点完整数据 叶子节点
↓
name + id
查询:
SELECT *
FROM user
WHERE id = 2;
流程:
主键索引
↓
id=2
↓
完整数据
查询:
SELECT *
FROM user
WHERE name = '李四';
流程:
idx_name
↓
李四
↓
id=2
↓
主键索引
↓
完整数据
后面这个:
二级索引
↓
主键索引
就是:
回表
二十三、为什么 SELECT * 可能增加回表?
例如二级索引:
CREATE INDEX idx_name
ON user(name);
执行:
SELECT *
FROM user
WHERE name = '张三';
二级索引只有:
name
+
id
但是你需要:
*
也就是:
age
address
phone
...
因此:
二级索引数据不够
↓
需要回表
这也是为什么:
实际开发中不建议无脑 SELECT *。
后面学习:
覆盖索引
以后这个问题会更清楚。
二十四、索引完整知识链
目前先掌握:
索引
↓
B+ Tree
↓
为什么使用B+树?
↓
树矮 + 叶子节点有序连接
↓
提高查询效率 + 适合范围查询
然后:
InnoDB索引
↓
┌──────┴──────┐
↓ ↓
聚簇索引 二级索引
↓ ↓
主键索引 普通索引
↓ ↓
完整行数据 索引列+主键
↓
需要完整数据
↓
回表
🎤 面试回答:为什么 MySQL 使用 B+ Tree?
可以回答:
InnoDB 索引主要使用 B+ Tree。B+ Tree 一个节点可以保存多个索引项,因此树的高度比较低,可以减少查询过程中需要访问的数据页数量。
同时 B+ Tree 的叶子节点按照索引顺序组织并相互连接,因此不仅适合等值查询,也非常适合范围查询。
🎤 面试回答:什么是聚簇索引?
可以回答:
InnoDB 的聚簇索引叶子节点保存完整的行记录,通常主键索引就是聚簇索引。
因此通过主键查询时,找到聚簇索引的叶子节点就可以获得完整的数据。
🎤 面试回答:什么是回表?
可以回答:
InnoDB 的二级索引叶子节点通常保存索引列和主键值。
如果查询需要二级索引中没有的字段,就需要先通过二级索引找到主键,再通过主键聚簇索引查询完整行数据,这个过程叫做回表。
🎯 面试追问
Q1:什么是索引?
答:
帮助数据库快速查找数据的数据结构,可以理解成数据库的目录。
Q2:InnoDB 索引主要使用什么结构?
答:
B+ Tree
Q3:为什么 B+ Tree 查询快?
答:
一个节点可以保存多个索引项
↓
树比较矮
↓
减少需要访问的数据页数量
Q4:为什么 B+ Tree 适合范围查询?
答:
因为:
叶子节点有序
+
叶子节点之间相互连接
找到范围起点后:
可以继续顺序扫描。
Q5:什么是聚簇索引?
答:
叶子节点保存完整行记录的索引,InnoDB 通常使用主键索引作为聚簇索引。
Q6:一个表可以有几个聚簇索引?
答:
一个
Q7:一个表可以有多个二级索引吗?
答:
可以
Q8:二级索引叶子节点存什么?
答:
通常:
索引列
+
主键值
Q9:什么是回表?
答:
二级索引
↓
得到主键
↓
主键聚簇索引
↓
查询完整数据
Q10:主键查询需要回表吗?
通常:
不需要
因为:
主键聚簇索引叶子节点
↓
已经保存完整行数据
Q11:二级索引查询一定回表吗?
不一定。
如果二级索引已经包含:
查询所需要的全部数据
就可能:
不需要回表
这就是后面要学的:
覆盖索引
Q12:索引是不是越多越好?
不是。
索引:
提高查询效率
但是:
占用空间
+
增加INSERT / UPDATE / DELETE维护成本
⚠️ 易错点
1. 索引不是越多越好
索引多
↓
查询可能更方便
但是
↓
写操作维护成本增加
2. 二级索引不是一定回表
错误:
二级索引查询
=
一定回表
正确:
查询需要的字段
二级索引已经全部包含
↓
可以不回表
这就是:
覆盖索引
3. 聚簇索引不是单独存一份“索引 + 数据文件”
可以简单理解:
聚簇索引的叶子节点
↓
就是完整行记录
4. B+ Tree 不只是为了等值查询
它还非常适合:
范围查询
排序
顺序扫描
❓ 自测
-
什么是索引?
-
为什么数据库需要索引?
-
InnoDB 索引主要使用什么数据结构?
-
为什么 B+ Tree 的树比较矮?
-
树比较矮有什么好处?
-
B+ Tree 的非叶子节点主要干什么?
-
B+ Tree 的数据主要在哪里?
-
为什么 B+ Tree 适合范围查询?
-
什么是聚簇索引?
-
InnoDB 主键索引的叶子节点存什么?
-
什么是二级索引?
-
二级索引的叶子节点通常存什么?
-
什么是回表?
-
SELECT * 为什么容易产生回表?
-
主键查询为什么通常不需要回表?
-
二级索引查询一定需要回表吗?
-
一个 InnoDB 表有几个聚簇索引?
-
一个表可以有多个二级索引吗?
-
为什么主键不建议设计得特别大?
-
没有主键时 InnoDB 怎么选择聚簇索引?
-
为什么索引不是越多越好?