用好 explain 妈妈再也不用担心我的 SQL 慢了

大家好,我是聪,一个乐于分享的小小程序员。在不久之前我写了一个慢 SQL 分析工具,可以用来分析 Java Mybatis 项目的 SQL 执行情况,其中刚好涉及到了 explain 的使用。感兴趣的可以了解一下。

Github 地址⭐:https://github.com/lhccong/sql-slow-mirror

那么开始我们今天的主题吧,今天的聪碰见了一个当面试官的朋友问我,用过 explain 吗?说说怎么分析的?

12.jpg

聪:一脸正经的回答道💡

聪:你好面试官,我当然用过 explain,我平时都会那它去查看 SQL 语句是否还能优化。接下来我从主要属性跟实际例子来讲解:

主要的属性

1) 🌱 id

查询中每个 SELECT 子句的标识符。简单查询的 id 通常为 1,复杂查询(如包含子查询或 UNION)的 id 会有多个。

2) 🌱 select_type

描述查询的类型。比如:简单查询显示为 SIMPLE,子查询显示为 SUBQUERY,UNION 中的第二个和后续查询显示为 UNION

3)🌱 table

表名称这个就不用再详细解释了吧哈哈。

4) 🌱 partitions

表示查询涉及到的分区。如果你有使用分区表的话才需要关注此字段。

5) 🌱 type(重点记忆⭐)

表示访问的类型,这里也可以看出你的 SQL 的性能。可能的值从最好到最差包括:systemconsteq_refrefrangeindexALL。其中 ALL 表示全表扫描,效率最低。

  • system:

    表示查询的表只有一行(系统表)。这是一个特殊的情况,不常见。

  • const:

    表示查询的表最多只有一行匹配结果。这通常发生在查询条件是主键唯一索引,并且是常量比较,以下是一个使用主键查找的例子:

    EXPLAIN SELECT * FROM employees WHERE employee_id = 12345;
    
  • eq_ref:

    表示对于每个来自前一张表的行,MySQL 仅访问一次这个表。这通常发生在连接查询中使用主键或唯一索引的情况下,例子如下:

    EXPLAIN SELECT * FROM employees e JOIN departments d ON e.department_id = d.department_id;
    
  • ref:

    MySQL 使用非唯一索引扫描来查找行。查询条件使用的索引是非唯一的(如普通索引),例子如下使用了非唯一索引进行查找:

    EXPLAIN SELECT * FROM employees WHERE department_id = 5;
    
  • range:表示 MySQL 会扫描表的一部分,而不是全部行。范围扫描通常出现在使用索引的范围查询中(如 BETWEEN>, <, >=, <=)。下面是范围查询:

    EXPLAIN SELECT * FROM employees WHERE salary BETWEEN 50000 AND 100000;
    
  • index:表示 MySQL 扫描索引中的所有行,而不是表中的所有行。即使索引列的值覆盖查询,也需要扫描整个索引。以下是使用索引扫描例子:

    EXPLAIN SELECT name FROM employees;
    
  • all(性能最差):表示 MySQL 需要扫描表中的所有行,即全表扫描。这通常出现在没有索引的查询条件中。以下是全表扫描例子:

    EXPLAIN SELECT * FROM employees;
    
6) 🌱 possible_keys

表示查询可能使用的索引列表。

7) 🌱 key

实际使用索引的长度。如果没有使用索引,该字段显示为 NULL

8) 🌱 key_len

这个字段表示使用的索引的长度。该值是根据索引的定义和查询条件计算的。

9) 🌱 rows

MySQL 会估计为了找到所需的行,需要读取的行数。该值是一个估计值,不是精确值。

10)🌱 filtered

显示查询条件过滤掉的行的百分比。一个高百分比表示查询条件的选择性好。

11)🌱 Extra

额外信息,如 Using index(表示使用覆盖索引)、Using where(表示使用 WHERE 条件进行过滤)、Using temporary(表示使用临时表)、Using filesort(表示需要额外的排序步骤)。

12.jpg

看完这个是不是一目了然了捏,那么接下来跟着我看看实际的分析例子吧!!

实际例子🌰

1.创建 employees 表
CREATE TABLE employees (employee_id INT PRIMARY KEY,first_name VARCHAR(50),last_name VARCHAR(50),department_id INT,salary DECIMAL(10, 2),hire_date DATE,INDEX (department_id)
);

我们要执行以下查询来查找部门 ID 为 5 且薪水在 50000 到 100000 之间的员工,并按薪水降序排序:

SELECT employee_id, first_name, last_name, salary
FROM employees
WHERE department_id = 5 AND salary BETWEEN 50000 AND 100000
ORDER BY salary DESC;
2.我们先使用 explain 分析计划进行分析:
EXPLAIN SELECT employee_id, first_name, last_name, salary
FROM employees
WHERE department_id = 5 AND salary BETWEEN 50000 AND 100000
ORDER BY salary DESC;

输出结果如下:

idselect_typetablepartitionstypepossible_keyskeykey_lenrefrowsfilteredExtra
1SIMPLEemployeesNULLrefdepartment_iddepartment_id4const500020.00Using where; Using filesort
3.分析执行计划

从执行计划中看出,typeref,表示使用了 department_id 索引,这是个非唯一索引。keydepartment_id 这个索引,而且 rows 为 5000,表示扫描了 5000 行匹配的 department_id = 5 的条件。从 Extra 看出在应用 WHERE 条件后,还需要进行文件排序来满足 ORDER BY 子句。

4.找出问题

尽管查询使用了索引,但由于索引不完全覆盖查询的条件和排序,查询需要进行额外的文件排序。这可能会导致性能瓶颈,特别是在结果集较大时。

5.优化解决它!

创建复合索引

创建一个包含 department_idsalary 的复合索引,这样可以覆盖查询的 WHEREORDER BY 条件:

CREATE INDEX idx_department_salary ON employees (department_id, salary);

复合索引可以使查询在扫描 department_id 列时,同时按 salary 列排序,避免额外的文件排序。

再次执行计划分析

优化后的 EXPLAIN 输出如下:

idselect_typetablepartitionstypepossible_keyskeykey_lenrefrowsfilteredExtra
1SIMPLEemployeesNULLrangeidx_department_salaryidx_department_salary5NULL500100.00Using where
6.分析优化后的结果

从新的 EXPLAIN 输出中可以看出:

  • type: range,表示使用范围扫描,这是个相对高效的访问类型。
  • key: idx_department_salary,表示实际使用了复合索引。
  • rows: 500,估计读取的行数减少了,因为索引更精确地覆盖了查询条件。
  • Extra: 仅显示 Using where,不再需要文件排序,因为索引已经覆盖了排序需求。

12.jpg

是不是分析起来很简单咧,完结撒花!!!!,除了新增联合索引的方式,你们还知道什么优化策略吗?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/12591.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C#】学习获取程序执行路径,Gemini 帮助分析

一、前言&#xff1a; 在Delphi中&#xff0c;如果想要获取当前执行程序的目录&#xff0c;程序代码如下&#xff1a; ExtractFilePath(ParamStr(0)); 今天在分析一个别人做的C#程序时看到了一段C#代码&#xff0c;意思是获取执行程序所在的文件目录&#xff1a; public stat…

基于区块链的Web 3.0关键技术研讨会顺利召开

基于区块链的Web3.0关键技术研讨会 2024年4月23日&#xff0c;由国家区块链技术创新中心主办的“基于区块链的web3.0关键技术研讨会”召开。Web3.0被用来描述一个运行在“区块链”技术之上的“去中心化”的互联网&#xff0c;该网络上的主体掌握自己数据所有权和使用权&#xf…

【回眸】git VS repo 区别

git VS repo 区别 1. git&#xff1a;Git是一个开源的分布式版本控制系统&#xff0c;用以有效、高速的处理从很小到非常大的项目版本管理。 2. Repo: Repo是谷歌用Python脚本写的调用git的一个脚本,Repo实现管理多个git库。 Git 常用命令 1. git init&#xff1a;在当前目…

【原创】java+springboot+mysql企业邮件管理系统设计与实现

个人主页&#xff1a;程序猿小小杨 个人简介&#xff1a;从事开发多年&#xff0c;Java、Php、Python、前端开发均有涉猎 博客内容&#xff1a;Java项目实战、项目演示、技术分享 文末有作者名片&#xff0c;希望和大家一起共同进步&#xff0c;你只管努力&#xff0c;剩下的交…

Vue的学习 —— <vue组件>

目录 前言 正文 一、选项式API与组合式API 二、生命周期函数 1、onBeforeMount() 2、onMounted() 3、onBeforeUpdate() 4、onUpdated() 5、onBeforeUnmount() 6、onUnmounted() 三、组件之间的样式冲突 四、父组件向子组件传递数据 1、定义props 2、静态绑定props…

C++青少年简明教程:赋值语句

C青少年简明教程&#xff1a;赋值语句 赋值语句是编程中最基本也是最常用的概念之一&#xff0c;它用于将一个值分配给一个变量。 使用等号&#xff08; 称为赋值运算符&#xff09;来给变量赋值&#xff0c;赋值语句的左边是要赋值的变量&#xff0c;右边是要赋给变量的值。C…

Docker 使用 CentOS 镜像

使用 docker run 直接运行 CentOS 7 镜像&#xff0c;并登录 bash。 C:\Users\yhu>docker run -it centos:centos7 bash Unable to find image centos:centos7 locally centos7: Pulling from library/centos 2d473b07cdd5: Pull complete Digest: sha256:be65f488b7764ad36…

GPT-4o:全面深入了解 OpenAI 的 GPT-4o

GPT-4o&#xff1a;全面深入了解 OpenAI 的 GPT-4o 关于 GPT-4o 的所有信息ChatGPT 增强的用户体验改进的多语言和音频功能GPT-4o 优于 Whisper-v3M3Exam 基准测试中的表现 GPT-4o 的起源追踪语言模型的演变GPT 谱系&#xff1a;人工智能语言的开拓者多模式飞跃&#xff1a;超越…

连接虚拟机的 redis

用Windows 的 Redis Insight 连接虚拟机的 安装redis发现连不上 我的redis是新安装&#xff0c;没有用户名密码&#xff0c;发现是ip问题 127 开头的被我注释了&#xff0c;换成了ifconfig查到的ip

vim命令大全(基础版)

创建一个py文件 vim cs.py一、命令模式 按Esc后&#xff0c;按shift&#xff1a;进入命令模式 :wq # 保存并退出 :q # 退出 :q! # 强制退出 :%d # 删除全部内容按两下d&#xff0c;删除光标所在行 按两下y&#xff0c;复制光标所在行 按一下p&#xff0c;粘贴复制内容到下…

Android性能:SurfaceFlinger与BufferQueue(3)

Android性能&#xff1a;SurfaceFlinger与BufferQueue&#xff08;3&#xff09; Android显示系统的组成可以概括为两大部分&#xff1a;绘制(DrawFrame)合成&#xff08;SurfaceFlinger HWC&#xff09; 绘制&#xff1a;Surface中空的 GraphicBuffer->CPU或者GPU通过Canv…

Python GUI开发- Qt Designer环境搭建

前言 Qt Designer是PyQt5 程序UI界面的实现工具&#xff0c;使用 Qt Designer 可以拖拽、点击完成GUI界面设计&#xff0c;并且设计完成的 .ui 程序可以转换成 .py 文件供 python 程序调用 环境准备 使用pip安装 pip install pyqt5-toolsQt Designer 环境搭建 在pip安装包…

Vue 常见通信

Vue 常见通信 1、父子通信 父传子 props&#xff0c;子传父 events&#xff08;$emit&#xff09;&#xff1b; 通过父链 / 子链 通信$parent / $children&#xff1b; $refs获取ref 可以访问组件实例方法&#xff0c;&#xff1b; 提供与注射provide / inject a t t r s …

使用Processing和PixelFlow库创建交互式流体太极动画

使用Processing和PixelFlow库创建交互式流体太极动画 引言准备工作效果展示代码结构代码解析第一部分&#xff1a;导入库和设置基本参数第二部分&#xff1a;流体类定义MyFluidDataConfig 类详解MyFluidData 类详解my_update 方法详解流体类定义完整代码 第三部分&#xff1a;太…

找数字-算法

解法一、数位模拟 比n大的最小数就是n1&#xff0c;当n1时&#xff0c;以下几种情况会导致n中1的个数发生变化&#xff08;或者不变&#xff09; 1.n的低位连续1的个数count>1&#xff0c;如1011&#xff0c;10111,1111等&#xff0c;加1后使得n中1的个数减少count-1个 解…

基于SVPWM的飞轮控制系统的simulink建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 基于SVPWM的飞轮控制系统的simulink建模与仿真。SVPWM的核心思想是将逆变器输出的三相电压矢量在两相静止坐标系&#xff08;αβ坐标系&#xff09;中表示&#xff0c;通过控…

Python3 数据类型详解:掌握数据基石,编写高效程序

Python3 中的基本数据类型包括整数&#xff08;int&#xff09;、浮点数&#xff08;float&#xff09;、布尔值&#xff08;bool&#xff09;、字符串&#xff08;str&#xff09;、列表&#xff08;list&#xff09;、元组&#xff08;tuple&#xff09;、集合&#xff08;se…

JAVA static注入 Util使用之Service注入

一般在Util等静态方法中调用Service或mapper&#xff0c;初始化后会出现空指针异常&#xff1a;java.lang.NullPointerException。 代码执行优先级的问题&#xff0c;在一个Java类中&#xff0c;存在着静态代码块&#xff0c;静态方法&#xff0c;构造函数,成员方法等等。不同…

golang中的类和接口

类 在 Go 语言中并没有类的概念&#xff0c;而是使用结构体来实现面向对象的特性。通过 type 关键字可以定义自定义类型&#xff0c;包括结构体类型。下面是一个简单的示例&#xff1a; package mainimport "fmt"// 定义一个结构体类型 type Person struct {Name s…

只对外公开必要的信息和步骤

隐藏内部结构 使对象的内部结构对外部不可见的内容的处理被称为封装化。只公开方法等使用者所需要的最低限度的接口&#xff0c;使用者只能通过外部接口进行访问。如此一来&#xff0c;使用该类的程序就无须知道其内部的具体实现。 通过封装化&#xff0c;不仅可以防止调用者…