数据库索引总览
索引是数据库用额外存储换查找与排序速度的结构。没有索引时,引擎往往只能扫描大量行;有合适索引时,可以快速定位到少量候选行,再读出真正需要的数据。
在 InnoDB 中,索引几乎总是 B+ 树(少数场景另有哈希、全文等)。理解索引 = 理解「B+ 树如何组织行」+「查询条件如何落到树的范围扫描」。
底层结构细节见:B 树与 B+ 树。
1. 没有索引会发生什么
表 users 有 1000 万行,查询:
SELECT * FROM users WHERE email = 'a@example.com';
- 无索引:引擎从第一行扫到最后一行(或扫完整段),最坏比较约 (O(n)) 次。
- 有
email上的 B+ 树索引:沿树下降到叶子,在有序叶子链表上定位等值或范围,I/O 与比较次数接近树高 + 匹配行数,通常远小于全表。
索引不是免费的:
- 占用磁盘与缓冲池内存
- 每次
INSERT/UPDATE/DELETE可能维护多棵树 - 过多、重复、无用的索引会拖慢写入并浪费空间
设计原则:为真正高频、选择性好、能缩小扫描范围的条件建索引,而不是「每列都建一个」。
2. InnoDB 的两类核心索引
2.1 聚簇索引(Clustered Index)
- InnoDB 表数据本身就按主键(或隐藏 row id)组织在一棵 B+ 树里。
- 叶子节点存完整行记录。
- 一张表只有一棵聚簇索引树。
- 主键查询通常最快:叶子即数据,无需再找别的树。
2.2 二级索引(Secondary Index / 非聚簇索引)
- 叶子节点存的是:索引列的值 + 主键值(不是完整行)。
- 若查询还需要索引列之外的字段,需要拿主键再回聚簇索引查一次,称为 回表(table lookup)。
- 若查询所需列都已在二级索引叶子中(含主键),可以 覆盖索引(covering index),避免回表。
二级索引叶子: [ email | primary_key ]
|
v 回表
聚簇索引叶子: [ pk | name | email | ... 整行 ]
以 users(id, name, email, age) 为例,可把 InnoDB 结构理解为:
表: users
列: id(PK), name, email, age
========================
1) 聚簇索引(PRIMARY, 按 id)
========================
叶子节点(整行):
[ id=1, name=Tom, email=tom@x.com, age=20 ]
[ id=2, name=Lucy, email=lucy@x.com, age=18 ]
[ id=3, name=Jack, email=jack@x.com, age=25 ]
========================
2) 二级索引(idx_email, 按 email)
========================
叶子节点(索引列 + 主键):
[ email=jack@x.com, pk=3 ]
[ email=lucy@x.com, pk=2 ]
[ email=tom@x.com, pk=1 ]
3. 回表 vs 覆盖索引
-- 可能回表:索引只有 email,还要 name
SELECT name FROM users WHERE email = 'a@example.com';
-- 覆盖:联合索引 (email, name) 时,叶子已有 name + pk
SELECT name FROM users WHERE email = 'a@example.com';
经验:
SELECT *很容易逼出回表;按需列更易覆盖。- 联合索引可以把「过滤列 + 高频投影列」排在一起,兼顾过滤与覆盖。
- 回表次数多时,优化器甚至可能放弃二级索引改走全表(成本模型权衡)。
4. 索引与查询形态的对应关系
| 查询形态 | 索引是否友好 | 说明 |
|---|---|---|
等值 = / IN | 友好 | 点查或少量分支 |
范围 >, <, BETWEEN, LIKE 'ab%' | 友好 | 叶子有序范围扫描 |
排序 ORDER BY 与索引顺序一致 | 友好 | 可能避免 filesort |
前缀模糊 LIKE '%ab' | 不友好 | 无法用 B+ 树从左定位 |
| 对索引列做函数/运算 | 通常失效 | 见索引优化 |
5. 本目录接下来读什么
读完索引后,建议接上 事务基础:写入路径上的锁与 MVCC 都挂在这些索引结构上。