索引¶
索引是数据库表中的一种 数据结构,用来加速数据的查找。可以把它类比成书的目录:不翻全书就能快速定位到某章
graph LR
A["查询 WHERE id = 100"] -->|无索引| B[逐行扫描全表]
A -->|有索引| C[索引快速定位行]
B --> D["慢 O(n)"]
C --> E["快 O(log n)"]
1 底层数据结构:B+ 树¶
大多数关系型数据库(InnoDB、SQLite、PostgreSQL)的索引基于 B+ 树
graph TD
A["根节点<br/>[1-50 | 51-100]"] --> B["[1-25]"]
A --> C["[26-50]"]
A --> D["[51-75]"]
A --> E["[76-100]"]
B --> B1[叶子: 数据]
C --> C1[叶子: 数据]
B+ 树特点:
- 矮胖多叉 → 树的高度很低(千万级数据也就 3~4 层)
- 所有数据都存在 叶子节点,叶子之间用 链表相连(便于范围查询)
- 查找复杂度 \(O(\log n)\),而全表扫描是 \(O(n)\)
某些场景(如 Memory 引擎、Redis)用 哈希索引:
| 索引类型 | 结构 | 优点 | 缺点 |
|---|---|---|---|
| B+ 树 | 多叉平衡树 | 支持范围查询、排序 | 略慢于哈希 |
| 哈希 | 哈希表 | 等值查询极快 \(O(1)\) | 不支持范围查询、排序 |
2 索引的类型¶
按功能分:
| 类型 | 说明 | 示例 |
|---|---|---|
| 主键索引 | 唯一标识每行,非空 | PRIMARY KEY (id) |
| 唯一索引 | 值不允许重复(可多个 NULL) | UNIQUE INDEX (email) |
| 普通索引 | 仅加速查询,无约束 | INDEX (name) |
| 联合索引 | 多个列组成一个索引 | INDEX (a, b, c) |
| 全文索引 | 文本搜索 | FULLTEXT (content) |
按存储方式分(InnoDB):
| 类型 | 说明 |
|---|---|
| 聚簇索引 | 主键索引,叶子节点存 整行数据;一张表只有一个 |
| 二级索引 | 非主键索引,叶子节点存 主键值,查数据要"回表" |
graph TD
A[二级索引 idx_name<br/>name → id] -->|找到主键id| B[聚簇索引<br/>id → 整行数据]
B --> C[返回完整记录]
3 索引如何工作¶
-- 假设有联合索引 INDEX (last_name, first_name)
SELECT * FROM users WHERE last_name = 'Zhang' AND first_name = 'San';
- 数据库在
(last_name, first_name)索引里按字典序查找 - 找到
('Zhang', 'San')对应的主键 id - 用主键 id 回表,取出完整行
4 最左前缀原则¶
联合索引 INDEX (a, b, c) 相当于建了三个索引:
(a)(a, b)(a, b, c)
-- ✅ 能用到索引
WHERE a = 1
WHERE a = 1 AND b = 2
WHERE a = 1 AND b = 2 AND c = 3
-- ❌ 用不到(跳过了 a)
WHERE b = 2
WHERE c = 3
WHERE b = 2 AND c = 3
核心:联合索引的列必须从最左边开始连续使用。所以设计联合索引时,把 区分度高、常用作等值条件的列放最前面
5 索引的优缺点¶
优点:
- 大幅提升查询速度
- 加速排序(ORDER BY)和分组(GROUP BY)
- 唯一索引保证数据唯一性
缺点(代价):
- 占用磁盘空间
- 写入变慢:每次 INSERT/UPDATE/DELETE 都要维护索引
- 索引过多会影响优化器选择,反而拖慢查询
什么情况不该建索引
| 场景 | 原因 |
|---|---|
| 表很小(几百行) | 全表扫描更快 |
| 频繁更新、很少查询的列 | 维护成本高于收益 |
| 区分度很低的列(如"性别") | 索引无法有效过滤,命中率低 |
| 大字段(长文本) | 索引体积巨大,通常用前缀索引代替 |
评论区
欢迎在评论区指出文档错误,为文档提供宝贵意见,或写下你的疑问