索引结构
为什么用 B+Tree?
| 数据结构 | 问题 |
|---|---|
| 二叉树 | 顺序插入退化为链表 |
| 红黑树 | 本质还是二叉树,层级深 |
| B-Tree | 非叶子节点也存数据,单节点容量小 |
| Hash | 不支持范围查询、排序 |
- 非叶子节点只存索引,单节点可存更多 key
- 叶子节点形成有序链表,支持范围查询
- 层级低(3 层可存 2000W+ 数据)
B+Tree 结构
索引分类
按数据结构
| 类型 | 说明 | 支持引擎 |
|---|---|---|
| B+Tree | 最常用,支持范围查询 | InnoDB, MyISAM |
| Hash | 精确匹配快,不支持范围 | Memory |
| Full-text | 全文索引 | InnoDB 5.6+, MyISAM |
按逻辑分类
| 类型 | 说明 | 关键字 |
|---|---|---|
| 主键索引 | 唯一 + 非空 | PRIMARY KEY |
| 唯一索引 | 值唯一,可以有 NULL | UNIQUE |
| 普通索引 | 无限制 | INDEX |
| 全文索引 | 文本搜索 | FULLTEXT |
InnoDB 索引分类
| 类型 | 说明 | 特点 |
|---|---|---|
| 聚簇索引 | 主键索引,叶子节点存完整数据 | 有且仅有一个 |
| 二级索引 | 非主键索引,叶子节点存主键值 | 可以有多个 |
- 有主键 → 主键索引
- 无主键 → 第一个 UNIQUE 非空索引
- 都没有 → 生成隐藏的 rowid
索引操作
回表与覆盖索引
回表查询
覆盖索引
SELECT *
索引失效场景
| 场景 | 示例 | 原因 |
|---|---|---|
| 最左前缀不匹配 | idx(a,b,c) 只查 b | 联合索引从左匹配 |
| 索引列运算 | WHERE YEAR(date) = 2024 | 函数破坏索引 |
| 类型隐式转换 | WHERE phone = 123(phone是字符串) | 类型不匹配 |
| 左模糊查询 | WHERE name LIKE ‘%张’ | 无法利用索引 |
| OR 条件无索引 | WHERE a=1 OR b=2(b无索引) | 全表扫描更快 |
| 范围查询右侧失效 | idx(a,b) WHERE a>1 AND b=2 | b 的索引失效 |
最左前缀法则
联合索引idx(a, b, c):
| 查询条件 | 是否走索引 | 使用的索引列 |
|---|---|---|
| a = 1 | ✅ | a |
| a = 1 AND b = 2 | ✅ | a, b |
| a = 1 AND b = 2 AND c = 3 | ✅ | a, b, c |
| b = 2 | ❌ | 无 |
| a = 1 AND c = 3 | ✅ | a(c失效) |
| a > 1 AND b = 2 | ✅ | a(b失效) |
索引设计原则
- 频繁查询的字段建索引
- 区分度高的字段优先(如手机号 > 性别)
- 字符串长字段用前缀索引
- 联合索引优于多个单列索引
- 索引不是越多越好,影响写入性能
- 避免在索引列上做运算或函数
前缀索引
SQL 提示
索引常见问题
Q1: 为什么主键建议自增?
自增主键顺序插入,性能好。非自增主键可能导致页分裂,影响性能。Q2: 为什么推荐使用整型主键?
- 整型比较快
- 占用空间小(二级索引存主键)
- UUID 36 字节,无序,性能差
Q3: 什么时候不建索引?
- 数据量小(< 几千条)
- 频繁更新的字段
- 区分度低的字段(如性别)