0

0

详解 MySQL 5.7 优化:Explain 执行计划

coldplay.xixi

coldplay.xixi

发布时间:2021-01-07 09:24:32

|

2817人浏览过

|

来源于CSDN

转载

mysql视频教程栏目介绍Explain 执行计划

详解 MySQL 5.7 优化:Explain 执行计划

推荐(免费):mysql视频教程

目录

  • 1. 介绍
  • 2. Explain 结果列详解
    • 2.1 id
    • 2.2 select_type
    • 2.3 table
    • 2.4 partitions
    • 2.5 type(非常重要)
    • 2.6 possible_keys
    • 2.7 key
    • 2.8 key_len
    • 2.9 ref
    • 3.10 rows
    • 2.11 filtered
    • 2.12 Extra

【注】

  • 当前系统环境: MySQL 5.7,其他版本略有不同,后期会抽时间单独说明。
  • 只介绍常见的场景,其他少见的场景暂不研究,如有需要可以去官方文档中查找。
  • 非入门,需要对 MySQL 的底层数据结构 B+ 树有一定的了解。
文档参考:MySQL 官方 Explain 文档

1. 介绍

使用 EXPLAIN 关键字可以模拟优化器执行 SQL 语句,并分析查询语句的性能瓶颈。

2. Explain 结果列详解

2.1 id

  • id 列的编号是 select 的序列号,一般有几个 select 就有几个 id(联表查询会有重复的 id),并且 id 的顺序是按 select 出现的顺序增长的。
  • id 越大则表示执行的优先级越高,id 相同(一般出现在联表查询)则从上往下执行,idNULL 最后执行。

2.2 select_type

select_type 表示对应行是简单的还是复杂的查询。常见的值有:

  • simple:简单查询,查询不包含子查询和union。

  • primary:复杂查询中最外层的 select 。

  • subquery:包含在 select 中的子查询(不在 from 子句中)

  • derived:包含在 form 子句中的子查询,MySQL 会将结果放在一个临时表中,也称为派生表。

  • union:在 union 中的第二个或之后的 select。

【注】在 MySQL 5.7 中,会对衍生表进行合并优化,如果要直观的查看 select_type 的值,需要临时关闭该功能(默认是打开的),下面的介绍中凡是涉及到衍生表的都需要该操作

# 关闭衍生表的合并优化(只对该会话有效)set session optimizer_switch='derived_merge=off'; # 打开衍生表的合并优化(只对该会话有效)set session optimizer_switch='derived_merge=on';

2.3 table

对应行查询的表。

【注】

  • from 子句中有子查询时,table 列为是 <derivenN> 的格式,表示这一行的执行的是 id = N 行的查询。
  • 当有 union 时,table 的数据为 <union M,N> 的格式,M 和 N 表示参与 unionselectid

2.4 partitions

未完待续。。。

2.5 type(非常重要)

  • type 表示这行查询的关联类型(访问类型,或查询类型),通过该值可以了解该行查询数据记录的大概范围。
  • 常见的值依次从最优到最差分别为:system > const > eq_ref > ref > range > index > ALL;一般我们要保证效率的话,要优化我们的语句至少使其达到 range 级别,如果可能的话做好优化到 refrange 一般用于范围查找,所以换句话说除了范围查找,其他的查询语句我们最好是优化到 ref 级别。

常见值说明

  • NULL : 表示 MySQL 能够在优化阶段分解查询语句,在执行阶段不用访问表和索引。

  • system / const: MySQL 能对某个查询部分进行优化并将其转化成一个常量(可以通过 show warnings 查看优化的结果),主要是查询主键(Primary Key)或唯一键索引(Unique Key)对应的记录,因为不存在重复,所以最多只能查询出一条记录,所以速度比较快。systemconst 的特例,当临时表里只有一条记录时为 system

    # 表里有一个主键id为1的记录 - constexplain select * from student where id = 1# 派生表里面只有一条记录 - systemexplain select * from (select * from student where id = 1) tmp# 注: 如果查询的列中有 text 类型,那么在这里 type 会变为 ALL ,# 因为无法使用内存临时表,只能在磁盘上创建临时表,所以性能上会有所损耗,效果等同于全表查询 ALL。
  • req_ref:当主键或唯一键索引的相关列并联接使用时(联表查询),最多匹配一条符合条件的记录。这是除了 const之外的最好的联接类型,简单的 select 查询不会出现 req_ref,更多出现在联表查询。

    # 虽然返回结果中有多条记录,但是在查询中一个学生id只对应一个班级,所以查询班级的时候为 req_ref,# 但是查询 student 的时候是 ALL,全表查询explain select * from student left join banji on student.id = banji.student_id

【注】在查询的过程中的返回结果如下:
在这里插入图片描述
当联接表查询时候会看作是一条查询 SQL,所以它们对应的 id 是一样的,当 id 都是一样的时候,按照从上到下的顺序依次执行,这里是先查询班级所有的学生(全表查询 ALL),然后根据学生id查找出学生对应的班级信息(req_ref)。

  • ref:当使用普通索引(Normal)或者是联合索引的部分前缀时,索引要和某个值进行比较,可能会找到多个符合条件的记录行,从辅助索引的根节点开始对比并找到相应的记录。

    # 简单的 select 查询,name 是普通索引(Normal Index)explain select * from student where name = '张三';# 简单 select 查询,banji_id (第一个) 和 student_id (第二个) 的联合索引EXPLAIN SELECT * FROM banji_student WHERE banji_student.banji_id = 3# 关联表查询# 包含 banji 表,banji_student 是班级与学生的关系表# 关系表中有 banji_id (第一个) 和 student_id (第二个) 的联合索引 idx_banji_stu_id 索引,# 以下查询只用到了联合索引的 banji_id (第一个)explain select * from banji_id from banji left join banji_student 
    	on banji.id = banji_student.banji_id
  • range:范围扫描,通常出现在 in,between,>,<,>= 等操作中,使用一个索引来检索给定范围的行。

    # 查询 id 大于 1 的学生信息explain select * from student where id > 2;
  • index

    • 扫描全索引就能拿到结果,一般是扫描某个二级索引辅助索引,除了主键之外的索引)。这种索引不会从主键索引树根节点开始查找,而是直接对二级索引的叶子节点遍历和扫描,从而查找出相应的记录行,速度比较慢;
    • 这种查询方式一般为使用覆盖索引,查询所需的所有结果集在二级索引主键索引中都有的情况下,由于二级索引一般比较小(因为二级索引非聚集的,其叶子节点是存放的主键索引相应的地址,而主键索引是聚集的,其叶子节点存放的是完整的数据集),所以优先走二级索引,这种情况通常比 ALL 快一些。
    • 在某些情况下,如果表的列数特别多,这个时候通过辅助索引查询的性能就不如直接使用主键索引效率高(如果查询了辅助索引的话,还会返回到主键索引中进行查找更多的字段,也就是回表查询,当然在某些情况下使用回表查询的性能也会比只使用主键索引的性能高),这个时候会走主键索引,这种情况也比 ALL 快。
    # student 表只有id主键,name 普通索引select * from student;# 这个时候会走 name 索引# 因为 name 是普通索引,所以如果加 where 的话可以达到 ref 级别select * from student where name = 'Ana'

    覆盖索引定义:覆盖索引一般针对于辅助索引,并不是真正的索引,只是索引查找的一种方式。如果 select 查询的字段都在辅助索引树中全部拿到,这种情况一般是使用了覆盖索引,不需要通过辅助索引树找到主键,再通过主键主键索引树里获取其它字段值。

  • ALL:全表扫描,扫描主键(聚簇、聚集)索引树的所有叶子节点,通常这种情况下要根据业务场景来增加其他索引进行优化。

    # id 为主键的 student 表,没有其他索引,该查询为 ALL.select * from student

2.6 possible_keys

possible_keys 主要显示查询可能用到哪些索引来查找,只是可能会使用,并不代表一定会使用。

腾讯交互翻译
腾讯交互翻译

腾讯AI Lab发布的一款AI辅助翻译产品

下载

常见值说明:

  • NULL: 没有相关索引,如果是 NULL 的话,可以考虑在 where 子句中创建一个适当的索引来提高查询性能,然后继续用 explain 查看其效果;也有可能出现 possible_keysNULL,但是 key 有值,实际走了索引。
  • 有列值:如果显示表中的某列,则表示可能会走这一列对应列值的索引;如果 possible_keys 有值,但是 key 显示 NULL这种情况一般存在于表中数据量不大的情况,因为 MySQL 语句优化器认为索引对此查询的帮助不大,从而选择了全表查询

2.7 key

  • key 表示 MySQL 实际采用哪个索引来优化对该表的查询。
  • 如果没有使用索引,则该列为 NULL,如果想强制 MySQL 使用或忽略 possible_keys 列中的索引,可以在查询中使用 force indexignore index.

2.8 key_len

显示了 MySQL 索引所使用的字节数,通过这个数值可以计算具体使用了索引中的哪些列(主要用于联合索引的优化)。

【注】索引最大长度是 768 字节当字符串过长时MySQL 会做一个类似左前缀索引的处理,将前半部分的字符提取出来做索引。

示例:一个学生与班级的关系表:banji_student,存在使用 banji_idstudent_id 两个列组合的联合索引,并且每个索引 int 都是 4 字节,通过 key_len 值为 4 可以知道只使用了联合索引的第一列:banji_id 来执行索引查找。

# 只使用了联合索引的第一列select * from banji_student where banji_id = 2

key_len 的计算规则如下:

  • 字符串:常见的是 char(n)varchar(n),从 MySQL 5.0.3 之后,n 均表示字符数,而不是字节数,如果是 UTF-8,一个数字或字母占1个字节,一个汉字占3个字节。

      描述
    char(n) 非汉字长度为 n,如果存放汉字长度为 3n 字节
    varchar(n) 非汉字长度为 n+2,如果存放汉字长度为 3n+2 字节;因为 varchar 是可变长字符串,需要 2 字节来存储字符串长度
  • 数值类型

      描述
    tinyint 长度为 1 字节
    smallint 长度为 2 字节
    int 长度为 4 字节
    bigint 长度为 8 字节
  • 时间类型

      描述
    date 长度为 3 字节
    timestamp 长度为 4 字节
    datetime 长度为 8 字节
  • NULL

    如果字段允许设置为 NULL,则需要 1 字节来记录是否为 NULLNot NULL 的列则不需要。

2.9 ref

显示了在使用 key 列中实际的索引时,表查找时所用到的列名和常量;常见的为 const 常量或索引关联查询的字段(列)名

# 使用了常量 2,所以在查询的时候 ref 为 constselect * from student where id = 2# 关联表查询# 包含 banji 表,banji_student 是班级与学生的关系表# 关系表中有 banji_id (第一个) 和 student_id (第二个) 的联合索引 idx_banji_stu_id 索引# 这里的 ref 为 test.id ,也就是指的是 banji.idexplain select * from banji_id from banji left join banji_student 		on banji.id = banji_student.banji_id

3.10 rows

显示预计查询的结果数,并不是真正的结果集中的记录(行)数,仅供参考。

2.11 filtered

未完待续。。。

2.12 Extra

这一列展示的是额外的信息,存在很多值,且在不同的场景下以及不同版本的 MySQL 所表示的意思也不同,只能是表示大概的意思并且仅做优化参考,这里只介绍常见的值。

  • Using index:使用覆盖索引,在 type 相同的情况下, Extra 的值为 Using index 要比为 NULL 性能高。

    比如 banji 表,存在 id,name,create_time 列,存在 id 主键name 普通索引

    # 覆盖索引,直接查询 name 对应的索引树就可以满足 select 后面的查询列select id,name from banji# 非覆盖索引,虽然也走了索引,但是进行了回表查询,以查询出 create_time 字段。select * from banji where name = '二年级'
  • Using where:使用 where 关键字来查询,并且对应的列没有设置索引,对应的 keyNULL

    这种情况一般要对查询的列添加相对应的索引来进行优化。

  • Using index condition:非覆盖索引查询并进行了回表,并且辅助索引使用了条件查询语句(where 或其他)。

    比如 banji_student 关系表,存在 id,banji_id,student_id,create_time 列,存在 id 主键banji_id 与 student_id 的组合(联合)索引

    # 进行了回表查询,以查询出 create_time 列,并且组合索引进行了范围查找select * from banji_student where banji_id > 3
  • Using temporaryMySQL 需要创建创建一个临时表来处理查询,出现这种情况一般要添加索引进行优化处理。

    # 如果 name 没有添加普通索引的话,则需要创建一个临时表来进行去重,Extra 值为 Using temporary# 如果添加了索引,则会走 name 对应的索引树,并且是覆盖索引,Extra 值为 Using indexexplain select distinct name from student
  • Using filesort:使用外部排序而不是索引排序,当数据较小的时候采用的是内存排序,当数据量较大的时候会频繁的访问磁盘,并将排序后的数据写入磁盘。

    # 如果 name 没有添加普通索引的话,则需要创建一个临时表来进行去重,Extra 值为 Using filesort# 如果添加了索引,则会走 name 对应的索引树,并且是覆盖索引,Extra 值为 Using indexexplain select name from student order by name
  • Select tables optimized away:使用聚合函数(例如 maxmin等)来访问存在索引的字段时,只访问索引树中已排好序的叶子,节点性能很高。

    # 比如使用聚合函数 min 查询最小的学生 id(主键)explain select min(id) from student

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
C# ASP.NET Core微服务架构与API网关实践
C# ASP.NET Core微服务架构与API网关实践

本专题围绕 C# 在现代后端架构中的微服务实践展开,系统讲解基于 ASP.NET Core 构建可扩展服务体系的核心方法。内容涵盖服务拆分策略、RESTful API 设计、服务间通信、API 网关统一入口管理以及服务治理机制。通过真实项目案例,帮助开发者掌握构建高可用微服务系统的关键技术,提高系统的可扩展性与维护效率。

76

2026.03.11

Go高并发任务调度与Goroutine池化实践
Go高并发任务调度与Goroutine池化实践

本专题围绕 Go 语言在高并发任务处理场景中的实践展开,系统讲解 Goroutine 调度模型、Channel 通信机制以及并发控制策略。内容包括任务队列设计、Goroutine 池化管理、资源限制控制以及并发任务的性能优化方法。通过实际案例演示,帮助开发者构建稳定高效的 Go 并发任务处理系统,提高系统在高负载环境下的处理能力与稳定性。

38

2026.03.10

Kotlin Android模块化架构与组件化开发实践
Kotlin Android模块化架构与组件化开发实践

本专题围绕 Kotlin 在 Android 应用开发中的架构实践展开,重点讲解模块化设计与组件化开发的实现思路。内容包括项目模块拆分策略、公共组件封装、依赖管理优化、路由通信机制以及大型项目的工程化管理方法。通过真实项目案例分析,帮助开发者构建结构清晰、易扩展且维护成本低的 Android 应用架构体系,提升团队协作效率与项目迭代速度。

83

2026.03.09

JavaScript浏览器渲染机制与前端性能优化实践
JavaScript浏览器渲染机制与前端性能优化实践

本专题围绕 JavaScript 在浏览器中的执行与渲染机制展开,系统讲解 DOM 构建、CSSOM 解析、重排与重绘原理,以及关键渲染路径优化方法。内容涵盖事件循环机制、异步任务调度、资源加载优化、代码拆分与懒加载等性能优化策略。通过真实前端项目案例,帮助开发者理解浏览器底层工作原理,并掌握提升网页加载速度与交互体验的实用技巧。

97

2026.03.06

Rust内存安全机制与所有权模型深度实践
Rust内存安全机制与所有权模型深度实践

本专题围绕 Rust 语言核心特性展开,深入讲解所有权机制、借用规则、生命周期管理以及智能指针等关键概念。通过系统级开发案例,分析内存安全保障原理与零成本抽象优势,并结合并发场景讲解 Send 与 Sync 特性实现机制。帮助开发者真正理解 Rust 的设计哲学,掌握在高性能与安全性并重场景中的工程实践能力。

223

2026.03.05

PHP高性能API设计与Laravel服务架构实践
PHP高性能API设计与Laravel服务架构实践

本专题围绕 PHP 在现代 Web 后端开发中的高性能实践展开,重点讲解基于 Laravel 框架构建可扩展 API 服务的核心方法。内容涵盖路由与中间件机制、服务容器与依赖注入、接口版本管理、缓存策略设计以及队列异步处理方案。同时结合高并发场景,深入分析性能瓶颈定位与优化思路,帮助开发者构建稳定、高效、易维护的 PHP 后端服务体系。

458

2026.03.04

AI安装教程大全
AI安装教程大全

2026最全AI工具安装教程专题:包含各版本AI绘图、AI视频、智能办公软件的本地化部署手册。全篇零基础友好,附带最新模型下载地址、一键安装脚本及常见报错修复方案。每日更新,收藏这一篇就够了,让AI安装不再报错!

169

2026.03.04

Swift iOS架构设计与MVVM模式实战
Swift iOS架构设计与MVVM模式实战

本专题聚焦 Swift 在 iOS 应用架构设计中的实践,系统讲解 MVVM 模式的核心思想、数据绑定机制、模块拆分策略以及组件化开发方法。内容涵盖网络层封装、状态管理、依赖注入与性能优化技巧。通过完整项目案例,帮助开发者构建结构清晰、可维护性强的 iOS 应用架构体系。

246

2026.03.03

C++高性能网络编程与Reactor模型实践
C++高性能网络编程与Reactor模型实践

本专题围绕 C++ 在高性能网络服务开发中的应用展开,深入讲解 Socket 编程、多路复用机制、Reactor 模型设计原理以及线程池协作策略。内容涵盖 epoll 实现机制、内存管理优化、连接管理策略与高并发场景下的性能调优方法。通过构建高并发网络服务器实战案例,帮助开发者掌握 C++ 在底层系统与网络通信领域的核心技术。

34

2026.03.03

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
MySQL 教程
MySQL 教程

共48课时 | 2.5万人学习

MySQL 初学入门(mosh老师)
MySQL 初学入门(mosh老师)

共3课时 | 0.3万人学习

简单聊聊mysql8与网络通信
简单聊聊mysql8与网络通信

共1课时 | 848人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号