SQL的explain的一些简单理解

概要

在之前HTT的面试中,面试官问了下SQL优化的事项,但是对于查询计划的回答并不是很好,所以在这里记录一下关于SQL explain的一些事项


EXPLAIN的作用

explain是SQL性能优化的关键工具,其内容包含了数据库如何具体执行一条SQL语句,通过分析其返回的执行计划,可以了解如何去执行一次查询,从而定位性能瓶颈,进行优化


基本用法

如下所示

sql 复制代码
EXPLAIN SELECT * FROM users WHERE name = 'John';

执行计划的关键字段

字段 重要性 说明与解读
type ⭐⭐⭐ (最关键) 访问类型 ,即 MySQL 找到所需行的方式。性能从优到劣常见为:system > const > eq_ref > ref > range > index > ALL。**ALL(全表扫描)**是需要极力避免的情况。
key ⭐⭐⭐ 实际使用的索引。如果此列为 NULL,则意味着查询没有使用索引,这通常是性能问题的信号。
rows ⭐⭐ MySQL 预估需要扫描的行数。这是一个基于统计信息的预估值,数值越小越好。
Extra ⭐⭐⭐ 额外信息 ,包含大量重要细节。这里可能会出现需要警惕的提示,如 Using filesort(需要额外排序)和 Using temporary(使用了临时表);也可能出现好的提示,如 Using index(使用覆盖索引,无需回表)。
possible_keys ⭐⭐ 查询可能使用的索引。
select_type ⭐⭐ 查询的类型,如简单查询(SIMPLE)、子查询(SUBQUERY)、联合查询(UNION)等。

一般而言,我们在工作中要看的是type,是否走了全表扫描,如果是全表扫描的话,性能可能会变得非常低,所以我们一般要避免这种情况发生。

除此之外,如果看到rows比较大的话,也有存在优化的可能。

select_type也是很重要的指标。


Type的类型以及详细解析

性能等级 类型 (type) 说明与解读
🟢 最优 system 表只有一行(系统表)。这是 const 类型的一个特例。
🟢 最优 const 通过主键或唯一索引进行等值查询,结果最多只有一行匹配。
🟢 最优 eq_ref 在多表连接中,使用主键或唯一索引作为关联条件,对于前表的每一行,后表只有一条记录与之匹配。
🟡 较优 ref 使用非唯一索引进行等值查询,返回匹配某个单独值的所有行。
🟡 中等 fulltext 使用全文索引进行搜索。
🟠 一般 ref_or_null 类似 ref,但查询条件中额外包含对 NULL 值的查找。
🟠 一般 index_merge 查询同时使用了多个索引,并对结果进行合并(取交集或并集)。
🟠 一般 unique_subquery 在一些 IN 子查询中,替换 eq_ref 类型,用于根据主键或唯一索引进行子查询。
🟠 一般 index_subquery unique_subquery 类似,但用于非唯一索引的子查询。
🔴 较差 range 使用索引进行范围扫描,例如使用 BETWEENIN>< 等操作符。
🔴 较差 index 全索引扫描,遍历整个索引树。虽然比全表扫描好,但数据量大时性能依然较低。
最差 ALL 全表扫描,没有使用任何索引。这是性能最差的情况,需要尽力避免。

通常情况下,我们希望SQL查询至少要达到range水平,争取达到ref,但是就从国内大部分从业者的水平来说,能有个range或者fulltext就烧高香了。


重点类型详解与一些示例

1、const,eq_ref和ref

这三个是高效查询中最常见的类型,区分它们的关键在于索引的唯一性

const:通过主键或者唯一索引定位唯一一行记录

sql 复制代码
EXPLAIN SELECT * FROM users WHERE id = 1; -- id 是主键[5](@ref)

eq_ref:在多表连接时,连接条件使用了被驱动表的主键或者唯一索引

sql 复制代码
-- 假设 orders 表的 user_id 字段与 users 表的主键 id 关联
EXPLAIN SELECT * FROM orders o JOIN users u ON o.user_id = u.id; -- u.id 是主键,对于 o 的每一行,u 只有一条匹配[2,6](@ref)

ref:使用普通索引进行等值查询,可能返回多行记录

sql 复制代码
EXPLAIN SELECT * FROM users WHERE name = 'Alice'; -- name 字段建有普通索引[1,7](@ref)

2、range

这是处理范围查询时能达到的最好类型,常见于使用了between,大于号,小于号,in等操作符的查询

sql 复制代码
EXPLAIN SELECT * FROM users WHERE age BETWEEN 18 AND 30; -- age 字段有索引[4](@ref)
EXPLAIN SELECT * FROM users WHERE id IN (1, 3, 5); -- 主键的 IN 查询[3](@ref)

3、index与all

这两种类型都需要扫描大量数据,是优化的重点对象

index:虽然也是全扫描,但是扫描的是索引树,在某些情况下(覆盖索引)效率还行。

sql 复制代码
-- 假设 birth_year 和 gender 是一个复合索引
EXPLAIN SELECT birth_year, gender FROM users; -- 查询的列都包含在索引中(覆盖索引),避免了回表[4](@ref)

all:全表扫描

不用说什么了,最差的情况

sql 复制代码
EXPLAIN SELECT * FROM users WHERE active = 0; -- active 字段没有索引[6](@ref)

如何优化type类型

优化目标应该是尽量避免出现 ALLindex ,并努力让查询达到 range及以上级别。

  • 为查询条件添加索引 :这是最直接有效的方法。确保 WHEREJOINORDER BY子句中的字段有合适的索引。

  • 避免索引失效的写法 :例如,避免在索引字段上使用函数或表达式(如 WHERE id + 1 = 5)、避免前缀模糊匹配(如 LIKE '%雪')、注意数据类型不一致导致的隐式转换。

  • 使用覆盖索引:让索引包含查询所需的所有字段,这样可以避免回表操作,提升查询速度。


SELECT_TYPE类型

类型 说明与解读
SIMPLE 简单的 SELECT 查询,不包含子查询或 UNION 操作。
PRIMARY 查询中最外层的 SELECT。如果查询包含子查询或 UNION,最外层的查询就会被标记为 PRIMARY。
SUBQUERY 在 SELECT 或 WHERE 列表中包含的子查询,该子查询不依赖于外部的查询。
DEPENDENT SUBQUERY 与 SUBQUERY 类似,但此子查询依赖于外部查询的结果集。需要特别注意,因为它可能对外部查询的每一行结果都执行一次,容易导致性能问题。
DERIVED 出现在 FROM 子句中的子查询(也称为派生表),MySQL 会递归执行这些子查询,把结果放在临时表里。
UNION 在 UNION 查询中,第二个及之后的 SELECT 语句会被标记为 UNION。
DEPENDENT UNION 与 UNION 类似,但出现在子查询中,且 UNION 的第二个或之后的查询依赖于外部查询。
UNION RESULT 从 UNION 操作的临时表(即合并后的结果集)中获取结果的 SELECT。

重点类型详解与示例

理解这些类型如何在实际查询中体现非常重要。

  • PRIMARYSUBQUERY/DERIVED

    在一个包含子查询的语句中,最外层的 SELECT是 PRIMARY。如果子查询在 FROM子句中,它会被标记为 DERIVED(派生表),MySQL 需要先执行这个子查询,将结果存入临时表,再对外部查询进行处理 。如果子查询在 WHERE子句中且独立于外部查询,则通常标记为 SUBQUERY

  • DEPENDENT SUBQUERY的性能关注点

    这种类型的子查询其执行依赖于外部查询的每一行结果。这意味着,如果外部查询返回了1000行数据,这个子查询就可能需要执行1000次。当数据量很大时,这会严重消耗性能,是需要重点优化的类型之一 。

如何结合使用

分析 EXPLAIN时,最好将 select_typeid字段结合来看。id值越大,执行优先级越高。如果 id相同,则执行顺序从上到下 。通过观察不同 idselect_type的组合,可以清晰地勾勒出整个复杂查询的执行步骤。


小结

这段时间十分压抑,三角洲打上了统帅,但是打太多觉得还是空虚,觉得还是写一写什么吧。

相关推荐
木辰風7 小时前
PLSQL自定义自动替换(AutoReplace)
java·数据库·sql
山峰哥10 小时前
SQL调优实战密码:索引策略与Explain工具深度破局之道
java·开发语言·数据库·sql·编辑器·深度优先
梅梅绵绵冰11 小时前
sql题库知识点
数据库·sql
阳光九叶草LXGZXJ11 小时前
达梦数据库-学习-49-DmDrs控制台命令(同步之EXEC模块)
linux·运维·数据库·sql·学习
root666/11 小时前
【Java-后端-Mybatis】DISTINCT 作用
数据库·sql·mybatis
x70x8011 小时前
SQL数据库和非SQL数据库
数据库·sql
Mikhail_G11 小时前
Mysql数据库操作指南——分组查询(零基础篇十三)
大数据·数据库·sql·mysql·数据分析
麦聪聊数据11 小时前
能源互联网的数据桥梁:构建安全、实时的电力数据共享 API 网关
数据库·sql·安全·低代码
l1t12 小时前
DeepSeek总结的 LEFT JOIN LATERAL相关问题
前端·数据库·sql·postgresql·duckdb
DarkAthena12 小时前
大表数据高效切片:基于主键的无排序Where条件生成算法
数据库·sql·算法·数据分片