🌈个人主页 :一条泥憨鱼 (欢迎各位大佬莅临)

前言:
在学习 MySQL 的过程中,"索引(Index)"一定是最核心、最重要的知识之一。
很多初学者第一次接触索引时,会觉得:
概念抽象
原理复杂
B+树难懂
优化不会写
但实际上:
索引的本质,就是帮助数据库"快速查找数据"。
你可以把它理解成:
书籍的目录
如果一本书没有目录,你想找某一页内容,就只能
一页一页翻
但如果有目录,你就能快速定位。
MySQL 索引也是同样的道理。
这篇文章将从:
什么是索引
为什么需要索引
索引底层原理
B+树结构
索引分类
索引失效
SQL优化
等多个角度,带你彻底理解 MySQL 索引。
一、什么是索引?
索引(Index)是数据库中一种:
用于提高查询效率的数据结构。
它的作用:
加快数据查询速度
例如:
我们有一张用户表:
java
CREATE TABLE user(
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(20),
age INT
);
插入10万条数据后:
如果执行:
java
SELECT * FROM user WHERE name='张三';
MySQL 会:
从第一条开始
一条一条找
这种方式叫:
全表扫描
效率非常低。
如果给 name 字段添加索引
java
CREATE INDEX idx_name ON user(name);
数据库就能快速定位数据,查询速度会大幅提升。
二、为什么索引能提高查询速度?
核心原因:
减少数据扫描次数
没有索引:
100万条数据
可能扫描100万次
有索引:
可能只需要扫描几次
这就像:
没有目录的书
你想找:
第500页内容
只能:
从第一页翻到500页
有目录的书
直接:
目录 → 第500页
效率差距巨大。
三、索引的底层数据结构
很多面试中都会问:
MySQL索引底层是什么?
答案:
B+Tree(B+树)
四、为什么不用数组或链表?
4.1 数组的问题
数组查找快:
支持下标随机访问
但是插入数据很慢。
例如:
1 2 4 5
插入3:
1 2 3 4 5
后面元素都要移动。
数据库频繁增删改,数组不适合。
4.2 链表的问题
链表插入快,但查询慢。
因为:
只能一个一个找
不适合数据库查询。
4.3 二叉树的问题
普通二叉树:
可能退化成链表。
例如:
1
\
2
\
3
\
4
查询效率很差。
五、B+树是什么?
MySQL 最常用的索引结构:
B+Tree
它是一种:
多路平衡搜索树
特点:
查询快
层级少
磁盘IO少
非常适合数据库
六、B+树结构特点
6.1 所有数据都在叶子节点
B+树中:
真正的数据:
只存储在叶子节点
非叶子节点只存索引。
6.2 叶子节点形成链表
叶子节点之间,会形成双向链表。
因此:
范围查询效率极高
例如:
java
SELECT * FROM user WHERE id BETWEEN 100 AND 200;
6.3 树高度低
B+树的一个节点能存很多数据。
因此:
树层级很少
通常:
3~4层
就能存千万级数据。
七、聚簇索引与非聚簇索引
这是 MySQL 高频面试题。
7.1 聚簇索引(Clustered Index)
InnoDB 中,主键索引就是聚簇索引。
特点:
数据和索引放在一起
例如:
PRIMARY KEY(id)
叶子节点,直接存整行数据。
7.2 非聚簇索引(二级索引)
普通索引:
CREATE INDEX idx_name ON user(name);
叶子节点存的是:
主键值
查询时,还要回到主键索引查数据。
这个过程叫:
回表查询
八、索引分类
8.1 主键索引
创建主键时自动生成。
java
CREATE TABLE student(
id INT PRIMARY KEY,
name VARCHAR(20)
);
特点:
唯一
不能为空
8.2 唯一索引
保证字段唯一。
java
CREATE UNIQUE INDEX idx_phone
ON user(phone);
例如:
手机号不能重复。
8.3 普通索引
最常见。
java
CREATE INDEX idx_age
ON user(age);
仅用于提高查询效率。
8.4 联合索引(复合索引)
多个字段共同组成。
java
CREATE INDEX idx_name_age
ON user(name,age);
适用于:
java
WHERE name=? AND age=?
8.5 全文索引
用于:
文本搜索
例如:
FULLTEXT(content)
九、创建索引
9.1 创建普通索引
java
CREATE INDEX idx_name
ON user(name);
9.2 创建唯一索引
java
CREATE UNIQUE INDEX idx_email
ON user(email);
9.3 创建联合索引
java
CREATE INDEX idx_name_age
ON user(name,age);
9.4 删除索引
java
DROP INDEX idx_name ON user;
十、查看SQL是否使用索引
使用:
java
EXPLAIN
例如:
java
EXPLAIN
SELECT * FROM user WHERE name='张三';
十一、EXPLAIN重要字段
11.1 type
SQL性能指标。
常见:
| type | 性能 |
|---|---|
| all | 全表扫描(最差) |
| index | 扫描索引 |
| range | 范围查询 |
| ref | 普通索引查询 |
| const | 主键查询(最好) |
11.2 key
使用了哪个索引。
11.3 rows
扫描行数越少越好。
十二、索引失效场景
很多时候,明明建了索引,但SQL还是很慢。
原因:
索引失效
12.1 对索引列使用函数
错误示例:
SELECT * FROM user
WHERE YEAR(create_time)=2025;
索引失效。
正确:
SELECT * FROM user
WHERE create_time BETWEEN
'2025-01-01'
AND '2025-12-31';
12.2 模糊查询以%开头
错误:
LIKE '%abc'
索引失效。
正确:
LIKE 'abc%'
12.3 字段类型不一致
例如:
name VARCHAR
但:
WHERE name=123
可能导致隐式转换,索引失效。
12.4 使用or
例如:
WHERE age=18 OR salary=5000
可能索引失效。
12.5 联合索引不满足最左前缀
例如:
(name,age,gender)
SQL:
WHERE age=20
无法使用联合索引。
因为:
没有从最左边开始
十三、最左前缀原则
联合索引最重要的规则。
例如:
(name,age,gender)
可以使用:
WHERE name=?
WHERE name=? AND age=?
WHERE name=? AND age=? AND gender=?
不能直接:
WHERE age=?
十四、覆盖索引
什么是覆盖索引?
即:
查询的数据
刚好都在索引中
不需要回表。
例如:
CREATE INDEX idx_name_age
ON user(name,age);
SQL:
SELECT name,age
FROM user
WHERE name='张三';
由于:
name age 都在索引中
因此:
无需回表
性能更高。
十五、索引不是越多越好
很多初学者认为索引越多越快,其实这是不对的。
15.1 索引占空间
索引需要额外存储。
15.2 更新会变慢
执行:
INSERT
UPDATE
DELETE
索引也要维护。并且索引越多,维护成本越高。
十六、索引优化建议
16.1 高频查询字段建索引
例如:
用户名
手机号
订单号
16.2 数据量小不需要索引
例如:
几十条数据
全表扫描更快。
16.3 区分度高的字段适合索引
例如:
手机号
身份证号
不适合:
性别
因为:
重复值太多
16.4 联合索引优于多个单列索引
推荐:
(name,age)
而不是:
name
age
16.5 尽量使用覆盖索引
减少回表。
十七、JavaWeb中的索引优化场景
17.1 登录功能
java
SELECT * FROM user
WHERE username=?;
username 应加索引。
17.2 商品搜索
java
SELECT * FROM product
WHERE category_id=?;
商品分类应加索引。
17.3 订单查询
java
SELECT * FROM orders
WHERE user_id=?
ORDER BY create_time DESC;
推荐联合索引:
(user_id,create_time)
十八、面试高频问题
18.1 为什么索引能提高查询效率?
因为减少了数据扫描次数。
18.2 MySQL索引底层是什么?
B+Tree
18.3 聚簇索引和非聚簇索引区别?
聚簇索引:
数据和索引在一起
非聚簇索引:
索引存主键值
需要回表
18.4 什么是回表查询?
先查普通索引:
再查主键索引。
18.5 什么是最左前缀原则?
联合索引必须从最左边字段开始使用。
十九、总结
索引是 MySQL 性能优化的核心。
它的本质:
帮助数据库快速查找数据
重点一定要掌握:
B+树
聚簇索引
联合索引
最左前缀
覆盖索引
索引失效
对于开发者来说,真正的高性能系统:
并不是:
代码写得多复杂
而是:
SQL是否高效
索引是否合理
数据库性能,往往决定整个系统的性能上限。