从零起步学习MySQL || 第七章:初识索引底层运用及性能优化(结合底层数据结构讲解)
一、什么是 MySQL 中的索引?
定义:
索引是数据库中一种用于 加快数据查询速度 的数据结构。
它就像一本书的“目录”一样,可以让数据库更快地找到你想要的数据,而不需要从头到尾扫描整张表。
简单比喻:
假设有一本 1000 页的书:
没有索引时:你找“第 732 页的某个内容”,只能一页页翻;
有索引时:你看目录就能直接跳转到对应页码。
在 MySQL 中,索引是存储引擎层(如 InnoDB)维护的一种 数据结构,常用的数据结构是 B+ 树。
二、为什么需要索引?
索引的主要作用:
加快数据查询速度
加快排序、分组等操作
加快表的连接(JOIN)
通过唯一索引保证数据唯一性
但要注意:
索引会占用额外的磁盘空间;
写入/更新/删除数据时也会维护索引,稍微降低写入性能。
所以:索引是“以空间换时间”的机制。
三、MySQL索引的分类
索引可以按多种维度分类:
我们主要从以下四种维度讲解:
1️⃣ 按 数据结构 分类
2️⃣ 按 物理存储 分类
3️⃣ 按 字段特性 分类
4️⃣ 按 字段个数 分类
① 按数据结构分类
类型 | 底层结构 | 特点 | 举例 |
---|---|---|---|
B+树索引(最常用) | B+ Tree | 有序、支持范围查询 | 普通索引、主键索引、唯一索引 |
Hash索引 | 哈希表 | 查找速度快,但不支持范围查询 | MEMORY 引擎中可用 |
R-Tree索引 | 空间索引结构 | 用于地理空间数据(GIS) | 在 MyISAM 中支持 |
Full-text全文索引 | 倒排索引 | 用于全文搜索 | 在 MyISAM、InnoDB 中都可用 |
举例:
-- 创建B+树索引(默认)
CREATE INDEX idx_name ON user(name);-- 创建全文索引
CREATE FULLTEXT INDEX idx_content ON article(content);
② 按物理存储分类
类型 | 是否和数据一起存储 | 特点 | 举例 |
---|---|---|---|
聚簇索引(Clustered Index) | 数据与索引存储在一起 | 主键索引,每个表只能有一个 | InnoDB 主键 |
非聚簇索引(Non-Clustered Index) | 索引与数据分开存储 | 存储的是数据地址或主键值 | 普通索引、唯一索引 |
举例:
-- InnoDB 聚簇索引
CREATE TABLE student (id INT PRIMARY KEY, -- 主键聚簇索引name VARCHAR(50),age INT,INDEX idx_name(name) -- 非聚簇索引
);
解释:
id
是主键 → 聚簇索引;idx_name
是普通索引 → 非聚簇索引。
③ 按字段特性分类
类型 | 特点 | 举例 |
---|---|---|
主键索引(Primary Key) | 唯一且不能为 NULL,每个表只能有一个 | PRIMARY KEY(id) |
唯一索引(Unique Index) | 值唯一但可为 NULL | UNIQUE INDEX idx_email(email) |
普通索引(Normal Index) | 无限制 | INDEX idx_name(name) |
全文索引(Full-text Index) | 用于文本匹配 | FULLTEXT(content) |
举例:
CREATE TABLE user (id INT PRIMARY KEY, -- 主键索引email VARCHAR(100) UNIQUE, -- 唯一索引name VARCHAR(50), INDEX idx_name(name), -- 普通索引FULLTEXT INDEX idx_bio(bio) -- 全文索引
);
④ 按字段个数分类
类型 | 特点 | 举例 |
---|---|---|
单列索引 | 只对一个字段建立索引 | INDEX idx_name(name) |
联合索引(复合索引) | 对多个字段联合建立索引 | INDEX idx_user(name, age) |
举例:
-- 单列索引
CREATE INDEX idx_name ON user(name);-- 联合索引
CREATE INDEX idx_name_age ON user(name, age);
注意:
联合索引遵循 最左前缀原则,即索引 (name, age)
实际上相当于对:
(name)
(name, age)
有索引效果,但对(age)
单独无效。
四、总结表格一览
分类维度 | 类型 | 示例 |
---|---|---|
按数据结构 | B+树索引、Hash索引、R-Tree、全文索引 | CREATE INDEX idx_name ON user(name) |
按物理存储 | 聚簇索引、非聚簇索引 | 主键索引 vs 普通索引 |
按字段特性 | 主键、唯一、普通、全文 | PRIMARY KEY(id) 、UNIQUE(email) |
按字段个数 | 单列索引、联合索引 | (name) 、(name, age) |
补充讲解:
补充一 、最左匹配原则(Leftmost Prefix Rule)
✅ 1. 定义
“最左匹配原则”是指:
联合索引会从最左边的字段开始匹配,只有按照最左的字段顺序使用索引,索引才会生效。
换句话说:
MySQL 从联合索引的 第一个字段 开始匹配条件,
当中途遇到 范围查询(如 >
, <
, BETWEEN
, LIKE 'xxx%'
) 时,就会停止继续匹配后面的字段。
✅ 2. 举例讲解
假设我们有如下表:
CREATE TABLE user (id INT PRIMARY KEY,name VARCHAR(50),age INT,sex CHAR(1),INDEX idx_name_age_sex(name, age, sex)
);
索引的顺序是:(name, age, sex)
。
📘 例1:完整匹配(使用索引)
SELECT * FROM user WHERE name = 'Tom' AND age = 20 AND sex = 'M';
✅ 按 (name, age, sex)
顺序依次匹配,用到了全部三列索引。
👉 索引使用情况:name → age → sex
📘 例2:部分匹配(仍然使用索引)
SELECT * FROM user WHERE name = 'Tom' AND age = 20;
✅ 用到了前两列 (name, age)
。
👉 索引使用情况:name → age
📘 例3:只用最左字段(仍使用索引)
SELECT * FROM user WHERE name = 'Tom';
✅ 用到了索引的第一列 name
。
👉 索引使用情况:name
📘 例4:跳过最左字段(索引失效)
SELECT * FROM user WHERE age = 20;
❌ 不符合最左匹配原则,因为没有使用 name
。
👉 索引 完全失效,需要全表扫描。
📘 例5:范围查询中断匹配
SELECT * FROM user WHERE name = 'Tom' AND age > 18 AND sex = 'M';
✅ 只会使用 (name, age)
索引部分;
❌ 不会使用 sex
,因为 age > 18
是范围查询。
👉 索引使用情况:name → age(停止)
📘 例6:顺序不一致但可优化
SELECT * FROM user WHERE age = 20 AND name = 'Tom';
✅ 虽然条件顺序不一致,但 MySQL 优化器会自动调整 顺序,依然可以使用 (name, age)
索引。
📘 例7:LIKE 前缀匹配可用索引
SELECT * FROM user WHERE name LIKE 'T%';
✅ LIKE 'T%'
等价于范围查询 'T' <= name < 'U'
,仍然可用索引。
但:
SELECT * FROM user WHERE name LIKE '%Tom%';
❌ 前面有通配符 %
,索引失效。
三、索引下推(Index Condition Pushdown, ICP)
✅ 1. 定义
索引下推(ICP) 是 MySQL 5.6 引入的一种 索引优化机制。
在没有 ICP 之前,MySQL 在通过索引定位到数据行后,会把这些行提取出来交给 Server 层 再做 WHERE
条件判断。
而有了 ICP 后,一部分 WHERE
条件判断可以 在存储引擎层完成,减少回表次数,提高性能。
✅ 2. 举例说明
仍然以这个表为例:
CREATE TABLE user (id INT PRIMARY KEY,name VARCHAR(50),age INT,sex CHAR(1),INDEX idx_name_age(name, age)
);
📘 示例1:没有索引下推的情况
SELECT * FROM user WHERE name LIKE 'Tom%' AND age = 20;
假设联合索引 (name, age)
:
name LIKE 'Tom%'
是范围查询;所以索引只能匹配
name
,不能直接匹配age
。
🔹 没有 ICP 时的执行逻辑:
通过索引扫描所有
name
以'Tom%'
开头的记录;每匹配一条记录,都回表取完整行;
再在 Server 层 判断
age = 20
;不符合的行被丢弃。
👉 缺点:回表次数太多(磁盘 IO 多)。
📘 示例2:启用索引下推的情况(ICP)
有 ICP 时:
通过索引扫描
name LIKE 'Tom%'
;在索引层直接判断
age = 20
;只有满足
age = 20
的行才回表取数据。
👉 优势:
减少回表次数;
大幅提升查询效率。
✅ 3. ICP 的触发条件
必须是 InnoDB 或 MyISAM 引擎;
必须是 联合索引;
查询语句中有 范围查询 + 其他条件;
其他条件可以在索引中判断的情况下才生效。
最左匹配 + 索引下推 对比总结
特性 最左匹配原则 索引下推(ICP) 定义 联合索引从最左字段开始依次匹配,中断后不再匹配 在存储引擎层提前过滤数据,减少回表 作用阶段 索引匹配阶段 存储引擎读取阶段 是否减少IO 否 是 是否加速查询 是 是 触发条件 联合索引 范围查询 + 其他索引列条件 执行计划标识 key=idx_xxx
Using index condition