mysql 索引面试通俗总结
一、什么是索引?
通俗理解
索引就像一本书的目录。
一本书有 1000 页,你想找“事务”相关内容:
- 没有目录:从第一页逐页翻,这叫全表扫描。
- 有目录:先找到“事务在第 500 页”,再直接翻过去,这叫使用索引查询。
所以索引本质上是:
帮助 mysql 快速找到数据的一种数据结构,是一种用空间换时间的设计。
索引会额外占用磁盘空间,而且新增、修改、删除数据时,也要同步维护索引。
面试回答
mysql 索引是一种帮助存储引擎快速查找数据的数据结构,可以理解为数据库表的目录。没有索引时,mysql 可能需要逐行扫描;有索引后,可以快速定位数据。不过索引需要占用空间,也会增加增删改时的维护成本,所以索引并不是越多越好。
二、索引有哪些分类?
面试中最常问的分类有四种。
1. 按数据结构分类
- b+tree 索引
- hash 索引
- full-text 全文索引
innodb 最常用的是 b+tree 索引。
2. 按物理存储分类
- 聚簇索引,也叫主键索引
- 二级索引,也叫辅助索引、非聚簇索引
3. 按字段特性分类
- 主键索引
- 唯一索引
- 普通索引
- 前缀索引
4. 按字段数量分类
- 单列索引
- 联合索引
这些分类并不冲突。例如一个索引可以同时是:
b+tree 索引+二级索引+普通索引+联合索引。
三、为什么 innodb 使用 b+tree?
通俗理解
可以把 b+tree 理解成商场的多级导航:
第一层:食品区、服装区、电器区
↓
第二层:饮料区、零食区、粮油区
↓
第三层:可乐、牛奶、果汁找可乐时,不需要把整个商场逛一遍,而是按照分类一层一层往下找。
b+tree 不能简单理解成二分法。
二叉树一个节点通常只有两个分支,而 b+tree 一个节点可以有很多个分支,所以它是一棵多叉树。分支越多,树就越矮,查找数据需要访问磁盘的次数就越少。
千万级数据的 b+tree 通常只需要维持在三四层左右,也就是说,找到一条数据通常只需要少量磁盘 i/o。
b+tree 的特点
- 非叶子节点主要保存索引,用来指路。
- 叶子节点保存最终数据或主键值。
- 叶子节点按照顺序连接,适合范围查询。
- 树比较矮,可以减少磁盘 i/o。
为什么不用普通二叉树?
二叉树每个节点只有两个分支。
数据量大时,树会比较高,查询一条数据可能需要访问很多层,磁盘 i/o 次数比较多。
为什么不用 hash?
hash 做等值查询很快:
where id = 1001
但是不适合范围查询:
where id between 1000 and 2000
hash 中的数据不是按照大小顺序排列的,而 b+tree 的叶子节点有序连接,更适合数据库常见的等值查询、范围查询和排序。
面试回答
innodb 使用 b+tree,主要是因为 b+tree 是多叉树,树的高度比较低,可以减少磁盘 i/o。它的数据集中存储在叶子节点,单个非叶子节点可以保存更多索引。同时叶子节点有序连接,非常适合范围查询和排序。相比之下,二叉树层数较高,hash 虽然等值查询快,但不适合范围查询。
四、什么是聚簇索引和二级索引?
假设有一张商品表:
create table product (
id bigint primary key,
product_no varchar(50),
name varchar(100),
price decimal(10, 2),
index idx_product_no(product_no)
);
聚簇索引
主键 id 对应的索引就是聚簇索引。
它的叶子节点保存的是:
id + product_no + name + price + 其他完整数据
可以理解为:
主键目录后面直接放着完整档案。
二级索引
product_no 对应的是二级索引。
它的叶子节点主要保存:
product_no + 主键id
可以理解为:
商品编码目录只记录商品编码和档案编号,完整档案还在主键目录中。
innodb 中,如果表有主键,就使用主键作为聚簇索引;没有主键时,会尝试选择不允许为 null 的唯一列;如果仍然没有,innodb 会生成隐藏的聚簇索引键。
面试回答
innodb 的主键索引属于聚簇索引,叶子节点保存完整行数据。普通索引属于二级索引,叶子节点保存索引字段和对应的主键值。因此通过二级索引查询完整数据时,可能还需要根据主键再次查询聚簇索引。
五、什么是回表?
执行:
select * from product where product_no = 'p1001';
查询过程:
先查询 product_no 二级索引
↓
找到对应的主键 id
↓
再根据 id 查询主键索引
↓
获得完整商品数据查了两棵 b+tree,这个过程就叫回表。
通俗理解
你先在“小区住户姓名目录”中查到:
张三住在 3 栋 502
然后再去 3 栋 502 找张三。
第一次查目录,第二次找完整信息,这就是回表。
面试回答
回表是指通过二级索引查询时,先在二级索引中找到主键值,再根据主键值去聚簇索引中查询完整行数据。因为查询了两次 b+tree,所以回表次数过多会增加磁盘 i/o。
六、什么是覆盖索引?
执行:
select id, product_no from product where product_no = 'p1001';
二级索引中本来就保存了:
product_no + id
查询需要的数据已经全部存在于二级索引中,因此不需要再查询主键索引。
这就叫覆盖索引。
通俗理解
你问物业:
张三住在哪一栋哪一户?
姓名目录里已经写了“3 栋 502”,物业直接回答,不需要再去张三家确认。
面试回答
覆盖索引是指查询需要的所有字段都能直接从索引中获取,不需要再回到聚簇索引查询完整数据。覆盖索引可以减少回表次数和磁盘 i/o。执行计划的 extra 中出现
using index,一般说明使用了覆盖索引。
七、什么是联合索引?
例如建立:
create index idx_status_create_time on orders(status, create_time);
这就是联合索引。
联合索引不是分别创建两个独立目录,而是按照多个字段共同排序:
先按照 status 排序 status 相同时,再按照 create_time 排序
例如:
待支付:
2026-07-01
2026-07-02
2026-07-03
已支付:
2026-07-01
2026-07-02八、什么是最左匹配原则?
假设存在联合索引:
(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;
以下查询无法正常利用这个联合索引的最左部分:
where b = 2; where c = 3; where b = 2 and c = 3;
通俗理解
假设一本电话簿按照:
省份 → 城市 → 姓名
进行排序。
你知道省份,就可以快速缩小范围:
湖南省
你知道省份和城市,更容易查:
湖南省 → 长沙市
但你只知道姓名:
张三
因为整本目录不是先按照姓名排序,所以很难直接定位。
联合索引需要从最左边字段开始匹配,本质原因是后面的字段只在前面字段值相同的情况下才局部有序。
特别注意
where 条件中的书写顺序通常不是关键。
下面两条 sql 一般没有本质区别:
where a = 1 and b = 2; where b = 2 and a = 1;
mysql 优化器通常会调整条件,关键是联合索引中是否包含最左边的字段。
面试回答
联合索引遵循最左匹配原则,因为联合索引首先按照第一个字段排序,第一个字段相同时才按照第二个字段排序。因此只有从联合索引最左边的字段开始查询,才能利用索引的有序性快速定位数据。
九、联合索引遇到范围查询怎么办?
假设有联合索引:
(a, b)
查询:
where a > 1 and b = 2;
通常 a 可以用于确定索引扫描范围,但进入 a > 1 的大范围后,b 在整个范围中不再保持全局有序,因此 b 通常不能继续用于缩小索引扫描区间。
可以先记住面试常见结论:
联合索引向右匹配时,遇到
>、<这类范围条件,后面的字段通常不能继续用于确定索引扫描范围。
但小林文章中也特别说明,>=、<=、between、like '前缀%' 在部分情况下仍可能继续使用后面的联合索引字段,最终应结合 mysql 版本和 explain 的 key_len 判断。
面试时不需要一开始讲得特别复杂,可以先回答:
范围查询字段本身可以使用索引,但范围查询之后的字段是否还能继续参与索引定位,需要结合具体运算符和执行计划判断。
十、什么是索引下推?
假设有联合索引:
(name, age)
查询:
select * from user where name like '郭%' and age = 25;
没有索引下推
mysql 先根据 name 找到一批主键,然后全部回表,拿到完整数据后再判断 age = 25。
查索引 → 回表 → 判断年龄 查索引 → 回表 → 判断年龄 查索引 → 回表 → 判断年龄
有索引下推
因为二级索引中已经存在 age,所以 mysql 可以先在索引中判断年龄。
查索引 ↓ 先过滤掉年龄不是25岁的记录 ↓ 只把满足条件的数据回表
索引下推的作用就是:
尽量在二级索引内部提前过滤数据,减少回表次数。
执行计划 extra 出现:
using index condition
通常表示使用了索引下推。
十一、什么时候应该创建索引?
适合创建索引的字段:
- 有唯一性要求的字段,例如订单号、商品编码。
- 经常出现在
where条件中的字段。 - 经常用于
join关联的字段。 - 经常用于
order by的字段。 - 经常用于
group by的字段。 - 多个字段经常一起查询时,可以考虑联合索引。
例如订单查询:
select id, order_no, status, create_time from orders where user_id = 1001 and status = 1 order by create_time desc;
可以根据实际查询考虑:
create index idx_user_status_time on orders(user_id, status, create_time);
索引不仅能用于筛选数据,也可以利用自身的有序性帮助排序。
十二、什么时候不建议创建索引?
1. 数据量特别少
表里只有几十条数据,全表扫描可能更快。
2. 字段重复值很多
例如性别字段:
男、女
通过索引查出全表接近一半的数据,意义不大。
3. 很少用于查询的字段
如果字段不用于 where、order by、group by 和关联查询,建立索引价值不大。
4. 经常发生修改的字段
每次修改索引字段,都需要维护 b+tree。
例如用户余额经常发生变化,一般不能只因为“可能查询余额”就随便给余额建立索引。索引会占用空间,并降低增删改性能。
十三、为什么主键推荐自增?
自增主键
1、2、3、4、5
新增数据时,通常直接追加到后面:
1、2、3、4、5、6
不需要频繁移动原有数据。
随机主键
原来:
1、3、5、9
突然插入:
7
需要插入到中间。如果数据页已经满了,可能发生页分裂:
原来一个数据页
↓
拆成两个数据页
↓
移动部分数据页分裂会增加额外开销,也可能导致空间利用率降低。
所以在没有特殊业务要求时,innodb 一般建议使用较短的自增主键。主键越短,二级索引占用的空间通常也越小,因为二级索引叶子节点需要保存主键值。
面试回答
innodb 的数据按照主键顺序存放。使用自增主键时,新数据通常顺序追加,可以减少数据移动和页分裂。如果使用随机主键,数据可能插入到已有数据页中间,增加页分裂和空间碎片。另外,二级索引会保存主键值,所以主键长度也不宜过大。
十四、常见索引失效场景
1. 左模糊查询
where name like '%郭';
2. 左右模糊查询
where name like '%郭%';
索引是从字符串左边开始排序的,不知道开头是什么,就很难快速定位。
下面这种前缀匹配通常可以使用索引:
where name like '郭%';
3. 对索引字段进行计算
where age + 1 = 26;
可以改成:
where age = 25;
4. 对索引字段使用函数
where year(create_time) = 2026;
可以考虑改成范围查询:
where create_time >= '2026-01-01' and create_time < '2027-01-01';
5. 隐式类型转换
字段是字符串:
phone varchar(20)
错误写法:
where phone = 13800138000;
更合理:
where phone = '13800138000';
6. 不符合联合索引最左匹配
有索引:
(user_id, status, create_time)
只查询:
where status = 1;
通常无法有效使用该联合索引的最左部分。
7. or 两边有一边没有索引
where user_id = 1001 or remark = '测试';
如果 user_id 有索引而 remark 没有索引,优化器可能放弃索引,选择全表扫描。
需要注意:
“存在这些写法”不代表百分之百不使用索引,最终是否使用索引,应通过
explain验证。
十五、如何判断 sql 是否使用了索引?
使用:
explain select * from orders where order_no = '202607280001';
重点看以下字段。
possible_keys
可能使用的索引。
key
实际使用的索引。
如果:
key = null
通常表示没有使用索引。
key_len
mysql 实际使用了联合索引中的多少内容。
rows
预计需要扫描多少行。
一般来说,扫描行数越少越好。
type
数据访问方式。
常见效率大致从差到好:
all → index → range → ref → eq_ref → const
重点记忆:
all:全表扫描,需要重点关注。index:扫描整个索引。range:索引范围查询。ref:使用普通索引查找。eq_ref:多表关联时使用主键或唯一索引。const:通过主键或唯一索引查询一条确定记录。
extra
需要重点关注:
using filesort
表示不能直接利用索引完成排序,需要额外排序。
using temporary
表示使用了临时表,经常出现在复杂排序或分组中。
using index
表示使用了覆盖索引,不需要回表。
using index condition
表示使用了索引下推。
面试高频问答速记
1. 索引是什么?
索引是帮助 mysql 快速定位数据的数据结构,可以理解为书的目录。它通过额外的存储空间提高查询效率,但也会增加增删改的维护成本。
2. 为什么使用 b+tree?
b+tree 是多叉树,树高比较低,可以减少磁盘 i/o;数据集中在叶子节点,叶子节点有序连接,适合范围查询和排序。
3. 聚簇索引和二级索引有什么区别?
聚簇索引的叶子节点保存完整行数据,二级索引的叶子节点保存索引字段和主键值。一张 innodb 表只有一个聚簇索引,但可以有多个二级索引。
4. 什么是回表?
先通过二级索引找到主键,再根据主键去聚簇索引查询完整数据,这个过程叫回表。
5. 什么是覆盖索引?
查询需要的字段都包含在索引中,可以直接从索引获得结果,不需要回表。
6. 什么是最左匹配原则?
联合索引按照从左到右的字段顺序排序,查询通常需要从最左边字段开始匹配,才能充分利用索引的有序性。
7. 什么是索引下推?
在遍历二级索引时,先利用索引中的其他字段过滤数据,减少不必要的回表次数。
8. 索引是不是越多越好?
不是。索引会占用磁盘空间,并且新增、修改、删除数据时需要维护索引,会降低写入性能。
9. 为什么推荐自增主键?
自增主键通常是顺序插入,可以减少数据移动和页分裂;同时主键较短,也可以减少二级索引占用的空间。
10. 如何排查索引有没有生效?
使用 explain,重点查看 type、key、key_len、rows 和 extra,判断是否使用索引、扫描多少行以及是否发生额外排序、临时表和回表。
一句话记忆
索引 = 目录 b+tree = 多层有序目录 主键索引 = 目录后面直接放完整档案 二级索引 = 目录里保存主键编号 回表 = 先查普通目录,再查主键档案 覆盖索引 = 普通目录里已经有完整答案 联合索引 = 多个字段组成一个有顺序的目录 最左匹配 = 必须从目录最左边开始查 索引下推 = 回表前先在目录中过滤 explain = 检查 mysql 到底怎么查数据
项目面试表达
在订单表中,我通常会根据实际查询场景设计索引。例如订单号具有唯一性,可以建立唯一索引;用户经常按照用户 id、订单状态和创建时间查询订单,可以考虑建立
(user_id, status, create_time)联合索引。同时避免直接使用select *,尽量通过覆盖索引减少回表。sql 上线前会使用 explain 检查实际使用的索引、扫描行数,以及是否出现using filesort或using temporary。
到此这篇关于mysql 索引面试通俗总结的文章就介绍到这了,更多相关mysql索引面试内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论