InnoDB 中索引类型有哪些?
InnoDB 中索引类型有哪些?
zhangchangsheng面试考察点
- 基础掌握度:面试官不仅仅是想知道索引类型的名称,更是想知道你是否理解不同索引的底层结构(聚簇 vs 非聚簇)、存储方式、查询效率差异。
- 性能优化意识:考察你是否清楚各类索引的适用场景,能否在实际表设计中根据业务特点选择合适的索引类型,避免索引失效或不必要的索引。
- 原理深度:是否理解 “回表”、”覆盖索引”、”最左前缀” 等核心概念,能否解释为什么某些查询快、某些查询慢。
核心答案
InnoDB 的索引从结构上分为 两大类,从功能上分为多种类型:
- 按结构分类:
- 聚簇索引(Clustered Index):主键索引,叶子存数据。
- 二级索引(Secondary Index):非主键,叶子存主键值。
- 按功能分类(二级索引):
- 普通索引(Index): 最基本的索引 。
- 唯一索引(Unique) :允许 NULL,值唯一 。
- 主键索引(Primary Key):不允许 NULL,唯一非空。
- 联合索引(Composite):多列组合索引 。
- 前缀索引(Prefix):字符串前 n 个字符。
- 全文索引(Full-text):文本搜索(5.6+ 支持)。
一句话总结:InnoDB 只有两种物理结构 —— 聚簇索引(主键)和 二级索引(非主键),其他都是功能层面的划分。
深度解析
一、聚簇索引 vs 二级索引
flowchart TB
subgraph CL["聚簇索引(主键索引)B+Tree"]
A1["非叶子节点存主键值<br/>如 50、25、75"] --> A2["叶子节点(数据页)<br/>存储完整行数据:id=10、name、age 等"]
end
subgraph SL["二级索引(非主键索引)B+Tree"]
B1["非叶子节点存索引列值<br/>如 'M'、'G'、'T'"] --> B2["叶子节点<br/>存储索引值 + 主键值:name='Alice'、id=10"]
end
B2 -->|"回表:按主键到聚簇索引再查一次"| A2
上图展示了聚簇索引和二级索引的核心结构差异:
- 聚簇索引(Clustered Index):
- 叶子节点直接存储完整的行数据,数据和索引合二为一
- 一张表只能有一个聚簇索引(通常是主键)
- 数据按主键顺序物理存储,范围查询效率高
- 如果没有显式定义主键,InnoDB 会选择第一个非空唯一索引;都没有则生成 6 字节的隐藏主键
- 二级索引(Secondary Index):
- 叶子节点存储索引列的值 + 主键值
- 一张表可以有多个二级索引
- 查询时如果需要其他列,必须 “回表” 查聚簇索引
二、回表查询过程
flowchart TB
A["SQL: SELECT * FROM user WHERE name = 'Alice'"] --> B["步骤 1:查二级索引(name)<br/>name = 'Alice' 命中,找到主键值 id = 10"]
B -->|"id = 10"| C["步骤 2:回表查聚簇索引(id)<br/>用 id = 10 找到完整行数据:id、name、age 等"]
C --> D["返回完整数据<br/>代价:需要扫描 2 次 B+Tree(二级索引 + 聚簇索引)"]
上图展示了回表查询的完整过程:
- 第一步:在
name列的二级索引中查找'Alice',找到对应的主键值id=10 - 第二步:拿着主键值
id=10回到聚簇索引中查找完整的行数据 - 性能代价:需要访问两棵 B+Tree,产生两次随机 I/O
优化方案 —— 覆盖索引:
-- 如果只需要 name 和 id,不需要回表 |
三、各类索引详解
1. 主键索引
-- 创建方式 |
特点:
- 是聚簇索引,叶子节点存储完整行数据
- 唯一且非空(
NOT NULL + UNIQUE) - 一张表只能有一个主键
- 推荐使用自增整数作为主键(减少页分裂)
2. 唯一索引
-- 创建方式 |
特点:
- 索引列的值必须唯一(允许 NULL,且多个 NULL 不冲突)
- 是二级索引,叶子节点存储主键值
- 查询时会使用索引优化
与主键的区别:对比项 主键索引 唯一索引 是否允许 NULL ❌ 不允许 ✅ 允许 每表数量 只能 1 个 可以多个 物理结构 聚簇索引 二级索引 存储内容 完整行数据 主键值
3. 普通索引
-- 创建方式 |
特点:
- 最基本的索引类型,无特殊约束
- 允许重复值和 NULL
- 是二级索引
适用场景:频繁作为查询条件的列,如WHERE、ORDER BY、JOIN的列。
4. 联合索引
-- 创建方式 |
flowchart LR
A["(Alice, 18) → id=10"] --> B["(Alice, 25) → id=30"]
B --> C["(Bob, 20) → id=20"]
C --> D["(Bob, 30) → id=40"]
D --> E["(Carol, 22) → id=50"]
E --> F["……"]
A -.-> G["排序规则:先按 name 排序,name 相同再按 age 排序"]
最左前缀原则:
-- ✅ 走索引 |
设计原则:
- 把区分度高的列放在左边
- 把常用的查询列放在左边
- 考虑 “覆盖索引” 优化
5. 前缀索引
-- 创建方式:只取前 10 个字符建索引 |
适用场景:
字符串列很长(如 URL、邮箱、大文本)
列的前几个字符区分度已经足够
注意:前缀索引无法用于
ORDER BY和GROUP BY无法用于覆盖索引(必须回表)
-- 如何选择前缀长度? |
6. 全文索引
-- 创建方式 |
特点:
- MySQL 5.6+ InnoDB 开始支持全文索引
- 用于文本内容的全文搜索
- 支持中文分词(需要配置
ngram解析器)
适用场景:文章内容搜索、商品描述搜索等。
四、索引设计最佳实践
-- ✅ 好的索引设计 |
面试高频追问
- 为什么 InnoDB 推荐使用自增主键?
- 聚簇索引按主键顺序存储,自增主键是顺序写入,减少页分裂
- 避免随机 I/O,提升写入性能
- 二级索引存储主键值,整数主键占用空间小(4 字节 vs UUID 36 字节)
- 什么是覆盖索引?有什么好处?
- 查询的列都在索引中,不需要回表查聚簇索引
- 减少一次 B+Tree 查找,降低 I/O
- 可以通过
EXPLAIN看到Using index
- 联合索引 (a, b, c),哪些查询能走索引?
- ✅
WHERE a = 1 - ✅
WHERE a = 1 AND b = 2 - ✅
WHERE a = 1 AND b = 2 AND c = 3 - ❌
WHERE b = 2(缺少最左列) - ❌
WHERE c = 3(缺少最左列) - ✅
WHERE a = 1 AND c = 3(a 走索引,c 不走)
- ✅
- 什么情况下索引会失效?
LIKE以%开头:WHERE name LIKE '%abc'- 对索引列做运算:
WHERE YEAR(create_time) = 2024 - 隐式类型转换:
WHERE phone = 13800138000(phone 是 VARCHAR) OR条件中有非索引列!=、<>、NOT IN、IS NOT NULL(不一定,看优化器)
常见面试变体
- “聚簇索引和非聚簇索引有什么区别?”
- “什么是回表?如何避免?”
- “联合索引的最左前缀原则是什么?”
- “为什么主键推荐用自增 ID 而不是 UUID?”
记忆口诀
聚簇存数据,二级存主键; 回表多一次,覆盖来优化。 联合看最左,前缀省空间; 自增主键好,UUID 乱顺序。
总结
InnoDB 物理上只有 聚簇索引(主键,存完整数据)和 二级索引(非主键,存主键值)两种结构。功能上分为主键索引、唯一索引、普通索引、联合索引、前缀索引、全文索引等。核心优化思路是:减少回表(覆盖索引)、遵循最左前缀(联合索引)、选择高区分度列、使用自增主键。
声明:本文转载自小哈的博客 《InnoDB 中索引类型有哪些?》,版权归原文作者所有。转载请保留本声明与原文链接。
评论
匿名评论隐私政策
WalineTwikoo
