好的,我们系统地梳理一下 MySQL 的索引,包括类型、作用、底层原理和使用场景。这样你对回表、辅助索引、聚簇索引等概念会更清楚。
1️⃣ 什么是索引
**索引 (Index)**是数据库为了加速查询而对一列或多列数据建立的数据结构,相当于书的目录,可以快速定位数据,而无需全表扫描。
作用:
- 提高查询速度 (WHERE、JOIN、ORDER BY、GROUP BY)
- 降低 I/O 消耗
- 支持排序和范围查询
代价:
- 占用额外存储空间
- 插入/更新/删除时需要维护索引,可能降低写入性能
2️⃣ MySQL 的主要索引类型
(1) 主键索引 (Primary Key / 聚簇索引)
- 特性:
- 唯一标识行
- 数据行和索引在同一个 B+Tree(聚簇索引)
- 示例:
CREATE TABLE users (
id INT PRIMARY KEY,
name VARCHAR(50),
age INT
);
- 查询
id时直接访问数据,不回表
(2) 唯一索引 (Unique Index)
- 特性:
- 保证列值唯一,可以有 NULL(多次,视数据库而定)
- 示例:
CREATE UNIQUE INDEX idx_email ON users(email);
(3) 普通索引 (普通非唯一索引 / Secondary Index)
- 特性:
- 加速查询,不保证唯一
- InnoDB 存储为 辅助索引,只存索引列 + 主键
- 示例:
CREATE INDEX idx_age ON users(age);
- 查询 age=18 时:
- 用 idx_age 找到主键 id → 回表拿其他列
(4) 复合索引 (Composite / Multi-column Index)
- 特性:
- 索引多个列,遵循 左前缀原则
- 对
(col1, col2),可以高效查询col1或(col1, col2),但单独查询col2效率不高
- 示例:
CREATE INDEX idx_name_age ON users(name, age);
(5) 全文索引 (Full-text Index)
- 特性:
- 用于文本搜索 (MATCH...AGAINST)
- 只能在 CHAR / VARCHAR / TEXT 类型
- 示例:
CREATE FULLTEXT INDEX idx_content ON articles(content);
(6) 空间索引 (Spatial Index)
- 特性:
- 用于几何类型 (如 POINT, POLYGON)
- 支持空间查询 (距离、交集等)
- 示例:
CREATE SPATIAL INDEX idx_location ON places(location);
3️⃣ MySQL 索引底层结构
- B+Tree 索引(最常用)
- 聚簇索引 (主键索引) 存储数据行
- 辅助索引 (普通索引) 存储索引列 + 主键
- 哈希索引
- Memory 存储引擎使用
- 只支持精确匹配,不支持范围查询
- 全文索引 / R-Tree / GiST / SPATIAL
- 针对特殊类型数据
4️⃣ 索引的选择原则
- 高选择性列建索引(列值重复少)
- WHERE、JOIN、ORDER BY、GROUP BY 用到的列建索引
- 频繁更新/插入的表慎用过多索引
- 考虑覆盖索引(查询列包含在索引中,避免回表)
5️⃣ 小结
| 索引类型 | 特点 | 作用 |
|---|---|---|
| 主键索引 PK | 聚簇索引,唯一,不可为 NULL | 唯一标识,快速查询 |
| 唯一索引 Unique | 唯一,可为 NULL | 保证数据唯一 |
| 普通索引 Index | 不唯一,辅助索引 | 加速查询 |
| 复合索引 Composite | 多列组合索引,左前缀有效 | 多列查询加速 |
| 全文索引 Fulltext | 用于文本搜索 | MATCH ... AGAINST |
| 空间索引 Spatial | 用于几何类型数据 | 空间查询 |
画一个 MySQL InnoDB 索引与回表示意图,把主键索引 (聚簇索引)、辅助索引 (普通索引)、回表流程都画清楚。
假设表结构:
CREATE TABLE users (
id INT PRIMARY KEY,
name VARCHAR(50),
age INT,
INDEX idx_age(age)
);
- **聚簇索引 (主键索引)**存储完整数据行
- **辅助索引 (idx_age)**只存索引列
age+ 主键id
索引示意图
┌───────────────────────────────┐
│ users 主表 (聚簇索引)│
│ id | name | age │
│-------------------------------│
│ 1 | Tom | 18 │
│ 2 | Lily | 20 │
│ 3 | Jack | 18 │
└───────────────────────────────┘
┌───────────────┐
│ idx_age 索引页 │ (辅助索引)
│ age | id │
│----------------│
│ 18 | 1 │
│ 18 | 3 │
│ 20 | 2 │
└───────────────┘
查询流程示意
1️⃣ 查询 id 列,不回表
SELECT id FROM users WHERE age = 18;
流程:
idx_age 索引页查 age=18 -> 得到 id=[1,3]
直接返回 id=[1,3],不回表
2️⃣ 查询 name 列,需要回表
SELECT name FROM users WHERE age = 18;
流程:
1. idx_age 索引页查 age=18 -> 得到 id=[1,3]
2. 回表到 users 主表,根据 id 查 name
id=1 -> Tom
id=3 -> Jack
3. 返回结果 [Tom, Jack]
直观理解
查询条件列在辅助索引 -> 找到主键 -> 再回主表查其他列
┌─────────┐
│ idx_age │
│ age=18 │
│ id -> 1 │
│ id -> 3 │
└────┬────┘
│ 回表
▼
┌───────────────┐
│ users 主表 │
│ id | name | age│
│ 1 | Tom | 18│
│ 3 | Jack | 18│
└───────────────┘
返回 name
✅ 总结:
- 聚簇索引 (主键索引):存储完整行,查询列在主键里不回表
- 辅助索引:只存索引列 + 主键,查询其他列需要回表
- 回表:从辅助索引跳到主表获取完整数据
如果你需要,我可以画一个 B+Tree 结构示意图,把 聚簇索引页、辅助索引页和回表过程 画得像数据库内部真实访问一样,这样你看 MySQL 索引原理就更直观了。
好,我给你画一个 B+Tree 结构示意图,展示 MySQL InnoDB 的 聚簇索引、辅助索引以及回表流程,让你更直观理解索引是如何查找数据的。
假设表结构
CREATE TABLE users (
id INT PRIMARY KEY,
name VARCHAR(50),
age INT,
INDEX idx_age(age)
);
- 主键索引 (聚簇索引) 存储完整数据行
- 辅助索引 (idx_age) 只存索引列
age+ 主键id
1️⃣ 聚簇索引 B+Tree(主表)
聚簇索引 B+Tree(主键 id)
[2]
/ \
[1] [3]
叶子节点存储完整行:
[1 | Tom | 18], [2 | Lily | 20], [3 | Jack | 18]
- 查询主键 id=3 → 直接找到叶子节点,不用回表
2️⃣ 辅助索引 B+Tree(idx_age)
辅助索引 B+Tree(age -> id)
[18, 20]
/ \
[18] [20]
叶子节点存储:
[18 | 1], [18 | 3], [20 | 2]
- 查询 age=18 → 找到 id=1 和 id=3
- 因为查询列是 name,不在辅助索引里 → 回表到聚簇索引查完整数据
3️⃣ 回表流程示意
查询:SELECT name FROM users WHERE age=18;
1️⃣ 辅助索引查 age=18 -> 得到 id=[1,3]
idx_age
┌─────┐
│18->1│
│18->3│
└─────┘
│
2️⃣ 回表查主键索引 (聚簇索引)
users 主表 (聚簇索引)
┌───────────────┐
│ 1 | Tom | 18 │
│ 3 | Jack | 18 │
└───────────────┘
│
3️⃣ 返回查询结果
[Tom, Jack]
4️⃣ 总结
- 聚簇索引:存数据,查询主键直接命中叶子节点
- 辅助索引:只存索引列+主键,查询其他列必须回主表 (回表)
- 回表:辅助索引 → 主键 → 数据行,增加 I/O
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。
