前言

距离上次更新过去了差不多一个月,主要一直在忙八股和项目,然后就是投简历改简历,我觉得这是一个瓶颈期,因为一直没有正反馈,也许许多人就栽在这段时期吧,迷茫、焦虑,尤其是面对这样的环境,我希望好好沉下心,打好基础,没有机会就先沉淀,不知道过些日子,再回头看看自己写下的这段话,会有什么感想。

欲渡黄河冰塞川,将登太行雪满山:李白在《行路难·其一》中用此句,以渡河被冰堵塞、登山被雪阻挡,象征人生道路上仕途受阻,充满艰难险阻。

EXPLAIN

Q:我们为什么要使用explain?

A:帮助我们分析sql语句的执行过程

我们想要知道一条sql语句的执行时间可以这样操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-- 1. 首先检查 profiling 功能是否开启,默认可能是关闭的
SELECT @@profiling;

-- 2. 如果为 0,则开启它
SET profiling = 1;

-- 3. 执行你的 SQL 语句
SELECT * FROM your_table WHERE ...;

-- 4. 查看所有已记录查询的列表及其总耗时
SHOW PROFILES;

-- 5. 查看上面列表中某个特定查询(比如 Query_ID 为 1)的详细耗时分解
SHOW PROFILE FOR QUERY 1;

这样太慢了,我们只需要分析那些“慢SQL”,那些影响我们程序执行的SQL,我们可以通过开启慢日志帮助分析

1
2
3
4
5
6
7
8
-- 1. 开启慢日志功能
SET GLOBAL slow_query_log = 'ON';

-- 2. 设置会被标记为“慢SQL”的执行阈值
SET GLOBAL long_query_time = 2;

-- 3. 设置日志的存储路径
SET GLOBAL slow_query_log_file = '/var/lib/mysql/slow.log';

好了,现在我们知道有哪些SQL会拖慢程序,我们想要优化它,那我们如何判断优化点在哪里呢?

这就要引入我们的explain关键字,他有一些参数可以帮助我们分析SQL语句的性能

explain各个字段代表的意思

  • id :select查询的序列号,包含一组数字,表示查询中执行select子句或操作表的顺序
  • select_type :查询类型 或者是 其他操作类型
  • table :正在访问哪个表
  • partitions :匹配的分区
  • type :访问的类型
  • possible_keys :显示可能应用在这张表中的索引,一个或多个,但不一定实际使用到
  • key :实际使用到的索引,如果为NULL,则没有使用索引
  • key_len :表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度
  • ref :显示索引的哪一列被使用了,如果可能的话,是一个常数,哪些列或常量被用于查找索引列上的值
  • rows :根据表统计信息及索引选用情况,大致估算出找到所需的记录所需读取的行数
  • filtered :查询的表行占表的百分比
  • Extra :包含不适合在其它列中显示但十分重要的额外信息

这么多,咱们也记不住呀,其实我们只需要关注那些特别影响性能的字段

重点字段以及优化

如果希望了解得更详细些可以看这篇文章MySQL explain 应用详解(吐血整理🤩) - 个人文章 - SegmentFault 思否

假设现在表结构是这样的

image-20250830232047705

分析下面这样一条SQL

1
2
3
4
explain select * from orders
left join order_detail od on orders.id = od.order_id
where user_id = '5'
order by od.amount

image-20250830232435340

type是最重要的字段,性能好不好主要就看走什么类型的查询,性能排序如下

  • system > const > eq_ref > ref > range > index > ALL

可以看到我们的案例走的是ALL全表扫描,这种类型性能最差,因为每个字段都要匹配一次

possible_keys字段显示我们的SQL并没有使用索引,所以key和key_len都显示为null,一般我们希望key_len的值越小越好

Extra字段会显示一些额外信息用来总结你使用了什么到这条SQL

Extra: 包含不适合在其他列显示的额外信息。常见的重要值:

  • Using filesort: 表示MySQL无法利用索引完成排序,需要额外的排序操作。通常出现在 ORDER BY 子句中。需要优化
  • Using temporary: 使用了临时表来保存中间结果。常见于排序(GROUP BY, ORDER BY)和子查询。对性能影响较大,需要优化
  • Using index好现象! 表示使用了覆盖索引(Covering Index),即所有需要的数据都在索引中,不需要回表查询数据行。
  • Using where: 表示在存储引擎检索行后进行了过滤。

我想针对该SQL进行优化,如果我针对user_id和amount、order_id建立单列索引(有关索引的知识在我上一期博客),它会变成这样,这样我们就成功将查询类型变成了ref,通常我们希望且能达到的类型也是ref

1
2
3
CREATE INDEX idx_user_id ON orders(user_id);
CREATE INDEX idx_amount ON order_detail(amount);
CREATE INDEX idx_order_id ON order_detail(order_id);

image-20250830234649320

或者我们尽可能使用主键进行查询,这样会直接在索引中找到对应信息,不需要回表查询

1
explain select user_id, amount from orders where id = 4;

image-20250830235917927

感谢你看到这里!