面试常问 SQL 优化八股文总结:慢查询、索引失效、回表、覆盖索引一次搞懂

在后端开发和面试中,SQL 优化几乎是绕不开的话题。

尤其是 MySQL 相关问题,经常会围绕以下几个点展开:

  1. 如何定位慢查询

  2. SQL 语句执行得很慢,如何分析

  3. 索引概念以及索引底层数据结构

  4. 聚簇索引和非聚簇索引、回表查询

  5. 覆盖索引,超大分页优化

  6. 索引创建的原则

  7. 什么情况下索引会失效

  8. 谈一谈你对 SQL 优化的经验

这篇文章就结合实际项目场景,把这些高频问题系统整理一遍,既适合面试复习,也适合平时查漏补缺。


1. 如何定位慢查询

在项目中,如果某个接口响应特别慢,第一步并不是马上去改 SQL,而是先定位到底是哪一段慢

我之前遇到过一个场景:

接口压测的时候响应时间很高,差不多到了 5 秒左右。这时候我们先从系统层面去看,而不是凭感觉盲猜。

常见定位方式

1)借助链路追踪 / 监控工具定位

如果系统中接入了像 SkyWalking 这样的监控工具,就可以很直观地看到:

  • 是哪个接口慢

  • 接口内部哪一段耗时高

  • 是否是某条 SQL 执行时间过长导致的

也就是说,先通过链路追踪把问题范围缩小,最后定位到具体 SQL。

2)开启 MySQL 慢查询日志

如果项目里没有像 SkyWalking 这样的监控系统,那么也可以直接使用 MySQL 自带的 慢查询日志

MySQL 提供了慢日志功能,可以设置一个阈值,比如:

  • 超过 2 秒 的 SQL

  • 自动记录到日志文件中

这样就能通过日志把执行慢的 SQL 找出来。

总结

定位慢查询,核心思路就是两步:

  • 先定位是哪个接口、哪个模块慢

  • 再定位到具体哪条 SQL 慢

一句话概括就是:
先用监控工具缩小范围,再用慢查询日志锁定 SQL。


2. SQL 语句执行得很慢,如何分析?

当我们已经找到了"慢 SQL",下一步就是分析它为什么慢。

这里最常用的工具就是 MySQL 自带的 EXPLAIN

EXPLAIN 的作用

EXPLAIN 可以查看一条 SQL 的执行计划,帮助我们判断:

  • 有没有命中索引

  • 是索引扫描还是全表扫描

  • 有没有回表

  • 有没有额外的排序、临时表等开销

重点关注哪些字段

1)key 和 key_len

这两个字段主要看是否命中了索引。

  • key:实际使用了哪个索引

  • key_len:使用索引的长度

通过这两个字段,可以判断:

  • 索引有没有生效

  • 联合索引有没有充分利用

  • 是否只命中了部分索引列

2)type

type 非常关键,它反映了 SQL 的访问方式。

常见从好到差大致可以理解为:

  • const

  • ref

  • range

  • index

  • all

其中:

  • index 说明是全索引扫描

  • all 说明是全表扫描

如果看到 all,一般就要警惕这条 SQL 是否还存在优化空间。

3)extra

extra 用来补充说明执行过程中的一些额外信息。

比如常见的:

  • Using where

  • Using index

  • Using filesort

  • Using temporary

如果这里出现了一些不理想的信息,比如额外排序、临时表、回表迹象,就说明这条 SQL 还有优化空间。

分析思路总结

当一条 SQL 很慢时,通常这样分析:

  1. 先用 EXPLAIN 看执行计划

  2. key / key_len 是否命中索引

  3. type 是否出现全表扫描或全索引扫描

  4. extra 是否有回表、排序、临时表等问题

  5. 再根据结果调整索引或重写 SQL


3. 索引概念以及索引底层数据结构

什么是索引?

索引(Index)本质上是帮助 MySQL 高效获取数据的一种数据结构。

它的核心作用主要有三个:

  • 提高数据检索效率,减少全表扫描

  • 降低数据库的 IO 成本

  • 利用索引的有序性,降低排序成本和 CPU 消耗

你可以把索引理解成一本书的目录。

如果没有目录,你只能一页一页翻;

有了目录,就可以快速定位到你想找的内容。

MySQL 索引底层结构

在 InnoDB 存储引擎中,索引底层采用的是 B+ 树

为什么是 B+ 树,而不是别的结构?

主要原因有几个:

1)层级更少,查询路径更稳定

B+ 树一个节点可以存储很多键值,所以树的高度通常比较低。

高度越低,磁盘 IO 次数就越少,查询效率也更高。

2)非叶子节点只存索引,叶子节点存数据

这样每个节点能容纳更多索引项,更适合磁盘场景。

3)天然适合范围查询

B+ 树的叶子节点之间通常通过双向链表连接,所以:

  • 范围查询效率高

  • 排序扫描效率高

这也是为什么数据库索引底层非常适合用 B+ 树,而不是简单的数组、链表或者普通二叉树。


4. 聚簇索引和非聚簇索引、回表查询

什么是聚簇索引?

聚簇索引,也叫聚集索引

它的特点是:

  • 数据和索引放在一起

  • B+ 树的叶子节点保存的是整行数据

  • 一张表通常只有一个聚簇索引

在 InnoDB 中,主键索引一般就是聚簇索引。

什么是非聚簇索引?

非聚簇索引,也叫二级索引或辅助索引。

它的特点是:

  • 索引和数据分开存储

  • B+ 树叶子节点保存的不是整行数据

  • 保存的是对应记录的主键值

  • 一张表可以有多个非聚簇索引

什么是回表查询?

当我们通过二级索引 查到主键值后,如果还需要查询完整记录,就需要再到聚簇索引中查一次整行数据。

这个过程就叫做 回表

回表过程可以这样理解:

  1. 先走二级索引

  2. 找到对应的主键值

  3. 再根据主键值去聚簇索引中查整行数据

这相当于查了两次,所以性能通常不如直接命中聚簇索引,或者使用覆盖索引。


5. 覆盖索引,超大分页优化

什么是覆盖索引?

覆盖索引指的是:
查询使用了索引,并且要返回的列,在这个索引中就已经全部能找到。

这样就不需要再回表了。

举个简单例子

如果根据主键 id 查询,并且返回的字段本身就在索引里,那么就可以直接从索引中拿到数据,一次扫描完成,性能会比较高。

覆盖索引的好处

  • 避免回表

  • 减少磁盘 IO

  • 提高查询效率

这也是为什么平时要尽量避免无脑写 select *

因为你返回的列越多,就越容易导致无法走覆盖索引。


超大分页为什么慢?

比如分页 SQL:

sql 复制代码
select * from user order by id limit 100000, 10;

当偏移量非常大时,MySQL 会先扫描前面大量数据,再取后面那一小部分,所以效率很低。

超大分页优化思路

常见做法是:

覆盖索引 + 子查询

先通过索引快速查出目标 id,再根据这些 id 去查完整数据。

例如:

sql 复制代码
select * 
from user 
where id in (
    select id from user order by id limit 100000, 10
);

更常见的写法也可以是:

sql 复制代码
select u.*
from user u
join (
    select id from user order by id limit 100000, 10
) t on u.id = t.id;

为什么这样更快?

因为子查询里只查 id,通常更容易走覆盖索引。

先把目标 id 缩小出来,再回表查完整数据,总体成本会低很多。


6. 索引创建的原则

索引不是越多越好,而是要建在最有价值的位置

常见原则如下:

1)数据量较大、查询频繁的表适合建索引

如果一张表本身数据量很少,全表扫描成本也不高,这时候加索引收益并不明显。

2)常作为查询条件的字段适合建索引

比如:

  • where

  • order by

  • group by

这些高频使用的字段,通常是优先考虑建索引的对象。

3)区分度高的字段更适合建索引

例如身份证号这种字段区分度高,索引效果通常比较好。

而像性别这种值很少的字段,索引价值往往不大。

4)字段内容较长时,可以考虑前缀索引

对于字符串很长的字段,直接建完整索引会占用较大空间,

这时可以考虑使用前缀索引

5)优先考虑联合索引

如果多个条件经常一起查询,那么比起建多个单列索引,通常更推荐建联合索引

因为联合索引:

  • 更符合真实查询场景

  • 可以减少索引数量

  • 还能提升组合查询性能

6)控制索引数量

索引会提升查询效率,但也会带来代价:

  • 占用更多磁盘空间

  • 增加写操作成本

  • 插入、更新、删除时要维护索引

所以索引不是越多越好,而是要控制数量。

7)尽量使用 NOT NULL

如果字段本身业务上不允许为空,尽量在建表时就加上 NOT NULL

这样在索引使用和执行判断上通常更清晰,也更利于优化器处理。


7. 什么情况下索引会失效

这一部分也是面试高频题。

1)违反最左前缀法则

对于联合索引,如果没有从最左列开始使用,索引可能无法生效。

例如联合索引:

sql 复制代码
(a, b, c)

如果你直接查 bc,通常就无法充分利用这个联合索引。

2)范围查询右边的列无法继续使用索引

例如联合索引是:

sql 复制代码
(a, b, c)

如果条件中 b 用了范围查询,比如:

sql 复制代码
where a = 1 and b > 10 and c = 5

那么 c 往往就无法继续有效利用索引。


3)在索引列上进行运算或函数操作

例如:

sql 复制代码
where age + 1 = 30

或者:

sql 复制代码
where date(create_time) = '2026-03-11'

这类写法会导致索引失效,因为 MySQL 需要先对字段做计算,无法直接利用原索引结构。


4)字符串不加引号,发生隐式类型转换

例如字段是字符串类型,但 SQL 写成:

sql 复制代码
where phone = 13800138000

而不是:

sql 复制代码
where phone = '13800138000'

这可能触发隐式类型转换,从而导致索引失效。


5)like 以 % 开头

例如:

sql 复制代码
where name like '%zhang'

这种写法无法利用普通索引,因为前缀不确定。

而下面这种通常还能利用索引:

sql 复制代码
where name like 'zhang%'

8. 谈一谈你对 SQL 优化的经验

如果面试官问"你平时是怎么做 SQL 优化的",可以从下面几个层面来回答。

1)表设计优化

在建表阶段就考虑:

  • 字段类型是否合理

  • 长度是否合适

  • 是否允许为空

  • 主键设计是否合适

因为很多 SQL 性能问题,本质上是表设计阶段就埋下了隐患。

2)索引优化

重点关注:

  • 是否给高频查询字段建了索引

  • 是否合理设计联合索引

  • 是否避免冗余索引

  • 是否减少回表

  • 是否尽量走覆盖索引

3)SQL 语句本身优化

例如:

  • 避免 select *

  • 避免在索引列上做函数、计算

  • 避免不必要的子查询

  • 避免导致索引失效的写法

  • 使用 EXPLAIN 分析执行计划

4)架构层面的优化

当数据量进一步增大,仅仅优化 SQL 可能还不够,这时还要考虑:

  • 主从复制

  • 读写分离

  • 分库分表

通过架构手段分摊数据库压力。

5)结合监控工具持续优化

线上环境中不能只靠猜,而要结合:

  • SkyWalking

  • 慢查询日志

  • SQL 执行计划

  • 压测工具

持续发现瓶颈,再有针对性地优化。


总结

SQL 优化不是只会背几个名词,而是要形成一套完整思路:

  • 先定位问题

  • 再分析执行计划

  • 然后判断是不是索引、回表、分页、排序等原因

  • 最后从表设计、索引设计、SQL 写法、架构层面综合优化

面试中关于 SQL 的问题,看起来分得很细,但本质上都是围绕这几件事:

  1. 能不能定位慢 SQL

  2. 能不能看懂执行计划

  3. 懂不懂索引和回表

  4. 知不知道索引失效场景

  5. 有没有实际优化思路

把这几个部分串起来,SQL 这块基本就比较完整了。

相关推荐
旷世奇才李先生1 小时前
043校园二手交易平台系统-springboot+vue
java·vue.js·spring boot
一叶飘零_sweeeet1 小时前
Java 8→21 全链路架构升级指南:核心特性、底层演进与生产级兼容避坑全解
java
码农阿豪1 小时前
从“多库掣肘”到“一库平川”:金仓KingbaseES的融合数据库深度体验
数据库
⑩-1 小时前
SaaS-Admin-项目场景题
java·数据库·spring boot
廋到被风吹走1 小时前
【Spring AI】Java 开发者构建企业级大模型应用(RAG/Agent)的官方一站式框架
java·人工智能·spring
NGC_66111 小时前
Java基础面试题2
java·开发语言·python
xgstb1 小时前
如何使用C#与SQL Server数据库进行交互
数据库·c#·交互
xb11321 小时前
# SQL基础知识学习指南
数据库·sql·oracle
马猴烧酒.1 小时前
【JAVA算法|hot100】贪心算法类型题目详解笔记
java·开发语言·ide·笔记·算法·spring·贪心算法