【高频面试题】SQL 查询慢怎么排查

✅ 精简版

顺序:

  1. 先拿到慢 SQL:从慢查询日志、数据库监控(Prometheus/Grafana、阿里云 RDS 慢 SQL)找到这条 SQL;
  2. explain看执行计划,重点看 type、key、rows、Extra;
  3. 看是否缺索引、索引失效、回表过多、全表扫描、索引覆盖;
  4. 再判断是不是锁等待:看show processlist /performance_schema,有没有长事务、行锁、表锁阻塞;
  5. 然后看服务器资源:CPU、内存、磁盘 IO,是不是数据库资源打满;
  6. 再看业务层面:是否数据量暴涨、分页深度、join 笛卡尔积,统计信息过时;
  7. 最后验证优化效果,压测观察。

完整排查步骤

1. 捕获慢 SQL(第一步,找到是哪条 SQL)

  • 开启 MySQL慢查询日志 slow_query_log ,设置阈值比如 long_query_time=1s,超过 1 秒自动记录。
  • 云数据库(RDS)直接后台看慢 SQL 报表。
  • 也可以通过 show processlist 实时看当前正在执行的长耗时 SQL。

注意:show processlist 只能看到正在运行的,已经执行完的要看慢日志。
重点看四个指标:执行时间/扫描行数/返回行数/排序、临时表或者锁等待

如果扫描了几百万行却返回了十几行,可能是过滤条件没有索引;

如果扫描的数据不多但是扫描时间很长,可能是锁等待。

2. explain 执行计划(最核心,必讲)

执行 explain select xxx,重点看这几个字段:

  1. type (优先级最高) system > const > eq_ref > ref > range > index > ALL
  • ALL:全表扫描,大表绝对红线,必须优化
  • range:范围查询,比如 where id>100,尚可,注意范围不要过大
  • ref:普通索引等值匹配,正常
  1. key :实际使用的索引,null 代表没有用到索引
  2. rows:预估扫描行数,数字越大越危险,预估要扫描多少行才找到数据
  3. Extra(额外信息,坑基本在这里)
  • Using filesort文件排序,没用到索引排序,需要额外排序,性能差
  • Using temporary:创建临时表,常见于 group by,消耗大
  • Using index索引覆盖,最佳!不需要回表
  • Using where:存储引擎返回数据后,MySQL 服务层再过滤

主要看标:

1.全表扫描还是走索引?

2.是否回表过多?(比如,避免select *, 设计覆盖索引)

3.排序和分页?(比如深分页,游标分页)

索引失效常见原因(面试官最爱追问)

  • 索引列做函数运算、隐式类型转换(字符串不加引号)
  • like %前缀通配 like '%abc'
  • or 两边有非索引字段
  • 联合索引最左匹配原则不满足
  • MySQL 优化器判断走索引代价更大,主动放弃索引,走全表扫描(比如筛选后数据占表 30% 以上)

什么是回表?

前提:InnoDB 索引分两种:主键索引(聚簇索引)二级索引(普通索引)

二级索引里面存的不是完整行数据,只存【索引列 + 主键】。通过二级索引找到主键之后,再拿主键去主键索引里读取完整行数据,这个二次查找的动作就叫回表。

1. InnoDB 聚簇索引原理

InnoDB 主键索引就是聚簇索引

  • 叶子节点 = 完整一行的所有数据
  • 表数据本身就放在主键索引的叶子节点,数据和主键索引绑定在一起

二级索引(普通索引、唯一索引):

  • 叶子节点只存:索引字段的值 + 主键 ID没有其他业务字段

举个例子

order 表:id(主键), orderNo, user_id, amount 建立普通索引:idx_orderNo(orderNo)

执行 SQL:

sql 复制代码
select * from `order` where orderNo = 'ORD123456';

执行过程:

  1. 在二级索引 idx_orderNo 找到 orderNo='ORD123456',拿到对应的主键 id=10001
  2. 拿着 id=10001 去主键索引(聚簇索引)叶子节点,读取整行全部字段 👉 第二步这个动作就是回表

回表本质:两次 B + 树查找,一次二级索引,一次主键索引,会产生额外 IO,影响性能。

2. 什么情况不会回表?------ 索引覆盖(Using index)

查询需要的所有字段,全部都包含在二级索引里面,不需要去主键索引查数据,就不会回表。

把索引改成联合索引:idx_orderNo(orderNo, user_id, amount)

sql 复制代码
select orderNo,user_id,amount from `order` where orderNo='ORD123456';

explain Extra:Using index,直接从二级索引拿到全部需要字段,不需要回表

3. 什么时候回表代价很高?

  • 查询出来结果集很大,大量行都要回表,大量随机 IO
  • 这也是 MySQL 优化器有时候有索引但是不走索引的原因: 如果筛选出大量数据,回表太多,优化器评估:直接全表顺序扫描比大量随机 IO 回表更快,于是放弃索引。

✅精简版

InnoDB 主键索引是聚簇索引,叶子节点保存完整行数据;

二级索引叶子节点只存索引列 + 主键。

当我们通过二级索引查询,需要的字段不在二级索引里面,就需要拿着主键去主键索引再次查找完整数据,这个过程叫回表。

如果查询的所有字段都在二级索引里,就是索引覆盖,避免回表,性能更好。

高频追问

Q1:主键查询会不会回表?

A:不会。主键直接走聚簇索引,一次 B + 树查找直接拿到完整行,不存在回表。

Q2:联合索引什么时候触发回表?

A:where 条件命中索引,但是select要查询的字段不在联合索引里面,就需要回表。

Q3:回表是随机 IO 还是顺序 IO?

A:随机 IO。二级索引查出来的主键大概率是离散无序的,去主键索引读取数据的时候,磁盘指针到处跳,随机 IO 开销远高于顺序扫描。

Q4:为什么 MyISAM 没有回表概念?

A:MyISAM 是非聚簇索引,主键索引和二级索引结构一样,叶子节点存的都是磁盘文件偏移地址,所有索引查找都是拿偏移量读文件,没有聚簇索引这个概念,所以不存在回表。

3. 排查锁 (锁等待、死锁)& 长事务

SQL 本身逻辑很快,但是被锁住,表现为查询卡住

sql 复制代码
show engine innodb status;
  • 长事务:事务开启很久不提交,占用行锁,阻塞后面的 DML / 查询(RR 隔离级别下 MVCC 读快照,普通 select 不加行锁;update/delete 会被阻塞)

注意:普通快照读不会被行锁阻塞;当前读(select ... for update)会被行锁阻塞。

  • 存在表锁、意向锁,大量事务等待锁,出现锁等待、事务回滚。

4. 服务器资源层面

  • CPU:MySQL CPU 100%,大概率大量排序、全表扫描、复杂计算
  • IO:磁盘 IO 打满,大量随机 IO,回表过多、缺少索引
  • 内存:buffer pool 太小,热点数据放不到内存,大量磁盘读取

5. 业务 SQL 写法问题

  1. 深度分页:limit 100000,10,需要扫描前面十万行;优化:主键 id 分页 where id>xxx limit 10
  2. join 关联没加索引,产生笛卡尔积,行数爆炸
  3. group by /order by 字段无索引,触发 Using filesort/temporary
  4. 一次性查询大量字段,不 select *,减少回表
  5. 统计信息过时,优化器选错索引(analyze table 更新统计信息)

6. 优化之后验证

优化后再次 explain,观察 type、rows;上线前压测,监控 QPS、RT、慢查询数量变化。


高频追问(背)

Q1:Using filesort 一定很差吗?

不一定。小数据量 filesort 影响很小;大表、大结果集出现 filesort 才是严重问题。本质是排序字段没有索引,MySQL 要在内存 / 磁盘做排序。

Q2:select * 有什么坏处?

  1. 读取不需要的字段,增加网络传输;
  2. 无法触发索引覆盖,必须回表,增加 IO;
  3. 表结构变更,业务容易出问题。

规范:只查业务需要的字段。

Q3:联合索引最左匹配原则是什么?

联合索引 idx_a_b_c(a,b,c),查询条件必须从左开始连续匹配 。 where a=? ✔ where a=? and b=? ✔ where b=? ❌ 跳过左边 a,索引失效 范围查询后面的字段不能走索引where a=? and b>10 and c=?,c 无法使用索引。

Q4:什么是索引覆盖?

查询需要的所有列,全部都在索引里面,不需要回主键索引查原始数据,Extra 显示 Using index,性能很高。 比如联合索引包含 select 后面所有字段 + where 条件字段。

Q5:为什么有时候有索引,但是 MySQL 不走索引?

MySQL 优化器会估算成本:如果这条 SQL 查询结果占表数据很大比例(比如 > 20%~30%),走索引需要大量回表随机 IO,优化器判断全表顺序扫描更快,主动放弃索引。

Q6:慢查询日志开启对性能影响大吗?

有一点点开销,线上生产环境一般可以开启,阈值合理设置(1s),只记录慢 SQL,不会大量写入。

Q7:MVCC 和锁的关系?普通 select 会不会被行锁阻塞?

InnoDB RR 隔离级别,普通 select 是快照读,读取历史版本,不加行锁,不会被行锁阻塞; select ... for update /select ... lock in share mode 是当前读,会加锁,会被阻塞。

相关推荐
扬大平仔1 小时前
# 小深:用 AgentScope Java 2.0 Harness 做私人助手(上)mysql
java·开发语言·mysql
蓝速科技1 小时前
口岸政务窗口双屏翻译机落地应用指南
运维·数据结构·数据库·人工智能·科技·政务
风哥2号1 小时前
数据库教程FGMT20‑Oracle容灾体系架构与数据库升级迁移方案
数据库·oracle·架构
wudongfang6661 小时前
mysql全量同步数据改增量
数据库·mysql
志栋智能1 小时前
超自动化安全的变更与配置安全管理
数据库·安全·自动化
toooooop81 小时前
thinkphp查询数据表最后的自增id
前端·javascript·数据库
风哥2号1 小时前
数据库教程FGMT19‑Oracle多租户容器架构与新特性总结
数据库·oracle·架构
garmin Chen2 小时前
MySQL精简面试题
数据库·后端·mysql·面试
anxiao_m2 小时前
局域网文件传输工具有哪些?4款主流工具实测对比测评
大数据·网络·数据库