MySQL 默认存储引擎 InnoDB ,下文若无特殊说明均指 InnoDB。索引本质:排好序的数据结构,用来加速查询,代价是占用磁盘、减慢增删改。
一、索引是什么
索引是存储引擎用于快速定位行数据的数据结构。
没有索引:全表扫描(逐行对比)
有索引:通过索引结构快速找到目标数据地址,再读取数据。
类比:一本书的目录,通过目录快速找到页码,不用逐页翻阅。
二、InnoDB 索引底层结构:B+Tree
1. 为什么不用二叉树、B 树、Hash?
-
二叉搜索树:极端退化成链表,高度很高,磁盘 IO 多;
-
平衡二叉树(AVL / 红黑树):树高依然大,IO 次数多;
-
B-Tree:数据和索引键全都存在所有节点,页存储利用率低;
-
Hash 索引 :等值查询快,不支持范围查询、排序、前缀模糊查询,InnoDB 不支持手动 Hash 索引;
-
B+Tree(InnoDB 选择)
- 所有数据只存在叶子节点;
- 非叶子节点只存索引键,内存可缓存更多索引,树更低;
- 叶子节点用双向链表相连,天然支持范围查询、order by、分页;
- 叶子节点有序,范围遍历非常高效。
B+Tree 结构要点
- 一页大小默认 16KB(InnoDB 页);
- 一页存放很多索引条目,树高度通常 3~4 层;
- 一次查询最多 3~4 次磁盘 IO。
三、InnoDB 两大索引类型:聚簇索引 & 二级索引(重点!)
1. 聚簇索引(主键索引)
InnoDB 必须有聚簇索引,一张表只能一个聚簇索引
规则:
- 如果显式定义
PRIMARY KEY→ 主键作为聚簇索引; - 没有主键,选第一个唯一非空索引作为聚簇索引;
- 都没有,InnoDB 自动生成隐藏 6 字节
rowid作为聚簇索引。
特点:
-
B+Tree 叶子节点 存放完整的一行数据;
-
索引顺序就是数据物理存储顺序;
-
查询主键:直接拿到整行数据 → 主键查找最快。
CREATE TABLE user(
id INT PRIMARY KEY, -- id 就是聚簇索引
name VARCHAR(20),
age INT
);
2. 二级索引(普通索引、辅助索引)
除聚簇索引以外所有索引都是二级索引。
关键特性:
✅ 二级索引叶子节点 不存完整数据,只存:索引键 + 主键值
重点记忆:通过二级索引查到主键,再拿主键去聚簇索引查找完整数据 → 回表查询
查询流程示例:
SELECT * FROM user WHERE name = '张三'
- 在 name 二级索引找到 name="张三",拿到主键 id;
- 使用 id 去聚簇索引 B + 树查找整行数据;
- 这一步就是回表,产生两次 B + 树查找。
覆盖索引(优化核心)
如果查询字段全部在二级索引里,不需要回表,称为覆盖索引。
-- name建有索引,查询只查name、id(id自带在二级索引叶子)
SELECT id,name FROM user WHERE name='张三';
-- ✅ 覆盖索引,无需回表,性能极高
执行计划里 Extra 显示 Using index 代表命中覆盖索引。
四、索引分类(语法层面)
1. 主键索引 PRIMARY KEY
- 唯一、非空;一张表仅一个;就是聚簇索引。
2. 唯一索引 UNIQUE
索引列值不能重复,允许一个 NULL;属于二级索引。
UNIQUE INDEX uk_phone(phone)
3. 普通索引 INDEX
最基础索引,允许重复、允许 NULL。
INDEX idx_name(name)
4. 联合索引(复合索引)
多个字段合在一起建立一个索引 INDEX idx_a_b_c(a,b,c)
👉 最左匹配原则(重中之重)
索引有序:先排序 a,a 相同排序 b,b 相同排序 c。
执行的查询语句是必须要有a
生效情况:
✅ where a=?
✅ where a=? and b=?
✅ where a=? and b=? and c=?
✅ where a=? and b>? and c=? (范围条件后面索引失效)
❌ where b=?
❌ where b=? and c=?
❌ where c=?
先按 a 排序;只有在同一个a的时候,不同b才是有序的。那么对于范围查询会出现多组不同的a,那么后面的b不一定有序,于是后面索引失效
口诀:遇到范围(> < between like 前缀 %),后面索引失效
5. 前缀索引
长字符串(varchar、text等),只取前面若干字符建立索引,节省空间
create index idx_email on table_name(email(10)); -- 只使用email字段的前10个字符建立索引
缺点:无法使用覆盖索引,不能用于 order by/group by
前缀索引的选择性
选择性 = 不重复值数量 / 总行数
值域范围:(0,1]
- 越接近 1:选择性越好,索引高效
- 越接近 0:大量重复,索引基本没用
前缀索引风险:截取前缀后,选择性下降。
如果前 N 位大量重复,索引失效、回表暴增。
6. 全文索引 FULLTEXT
针对大文本(varchar/text)分词检索,替代模糊 %xxx%,不适合超大并发场景,一般业务优先 Elasticsearch。
五、索引失效常见场景
-
联合索引不满足最左前缀
-
索引列参与运算、函数调用
WHERE YEAR(create_time) = 2026; -- 失效
WHERE create_time >= '2026-01-01'; -- 正确写法 -
隐式类型转换
-- phone字段用的是字符串类型的索引,但条件传入数字,自动发生隐式类型转换,导致索引失效
WHERE phone = 13800138000; -
模糊匹配
like '%关键词'通配符在前不能走索引;like '关键词%'可以走索引 -
使用
or,一侧没有索引(可改用 union all 优化) -
not in / != / is not null大多情况无法有效利用索引 -
MySQL 优化器判断全表扫描更快,主动放弃索引(数据量占表 30% 左右常出现)
六、索引优缺点
✅ 优点
- 大幅加快
WHERE条件查询 - 加速
ORDER BY / GROUP BY / DISTINCT排序分组 - 唯一索引保证数据唯一性
❌ 缺点
- 占用磁盘空间
- DML 操作(insert/update/delete)需要维护 B + 树,降低写入性能
- 过多索引导致维护成本极高
七、建立索引最佳实践
- 优先给 WHERE、JOIN、ORDER BY、GROUP BY 的字段建索引;
- 联合索引把区分度高、等值查询放前面,范围条件放最后;
- 尽量使用覆盖索引,避免回表;
- 字符串优先前缀索引,不要对超长字段建完整索引;
- 不建无用索引,定期清理长期不用的索引;
- 主键尽量使用自增 INT/BIGINT(有序插入,避免 B + 树频繁分裂;UUID 主键随机插入会大量页分裂,性能差)。
八、补充:MyISAM 索引简单区别
MyISAM没有聚簇索引,全部是二级索引;
索引文件 (.MYI) 和数据文件 (.MYD) 分离;
叶子节点存储数据行的物理文件偏移地址;
不支持事务、崩溃不安全,业务开发基本不用。