Carry の Blog Carry の Blog
首页
  • Nginx
  • Prometheus
  • Iptables
  • Systemd
  • Firewalld
  • Docker
  • Sshd
  • DBA工作笔记
  • MySQL
  • Redis
  • TiDB
  • Elasticsearch
  • OpenClaw
  • Hermes Agent
  • Claude Code
  • MySQL8-SOP手册
  • MySQL实战45讲学习笔记
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Carry の Blog

好记性不如烂键盘
首页
  • Nginx
  • Prometheus
  • Iptables
  • Systemd
  • Firewalld
  • Docker
  • Sshd
  • DBA工作笔记
  • MySQL
  • Redis
  • TiDB
  • Elasticsearch
  • OpenClaw
  • Hermes Agent
  • Claude Code
  • MySQL8-SOP手册
  • MySQL实战45讲学习笔记
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • MySQL

    • MySQL 运维知识地图:从入门配置到高可用排障
    • MySQL8 配置文件 my.cnf 重要参数解读
    • MySQL 导出 CSV 中文乱码:字符集链路从头讲一遍
    • MySQL 角色管理
    • MySQL网络抓包审计
    • MySQL 性能压测:Sysbench 1.0 实战
    • MySQL Router 实现读写分离
    • Gh-ost重建表,清除表碎片率
    • MySQL MGR配合MySQL-router实现innodb-cluster
    • MySQL 快速分析binlog定位问题
    • MySQL执行计划分析
      • MySQL执行计划分析
        • 使用 EXPLAIN 命令
        • 执行计划的各个字段解释
        • 关键访问类型详解
        • 优化建议
        • 实际案例分析
        • 配合慢查询日志定位问题:全量记录所有语句
    • DBA常用SQL和命令整理备查
    • 单表数据同步方案选型:为什么不该用 mysqldump 做「实时同步」
    • MySQL的事务隔离级别
    • MySQL存储过程批量生成数据
    • MySQL insert on duplicate key update,replace into , insert ignore的理解
    • MySQL不同字符集之间的区别和选择
    • MySQL为什么有时候会选错索引
    • MySQL死锁问题
    • MySQL使用SQL语句查重去重
    • MySQLdump逻辑备份
    • MySQL 基于 GTID 主从复制:跳过异常事务的正确姿势
    • MySQL8快速克隆插件使用指南
    • MySQL8双1设置保障安全
    • MySQL锁
    • innodb cluster安装
    • OPTIMIZE TABLE 和 ANALYZE TABLE 的区别:用实测数据说话
    • MySQLReplicaSet 安装
    • 脚本实现MySQL ReplicaSet 高可用
    • MySQL 的 Left join、Right join 和 Inner join 的区别
  • Redis

  • Keydb

  • TiDB

  • MongoDB

  • Elasticsearch

  • Kafka

  • victoriametrics

  • BigData

  • Sqlserver

  • 数据库
  • MySQL
Carry の Blog
2022-03-10
目录

MySQL执行计划分析

版本说明

本文写于 2022-03,基于 MySQL 8.0。
EXPLAIN 核心字段含义未变,经 2026-07 复核仍适用。

# MySQL执行计划分析

在数据库性能优化中,分析和理解SQL语句的执行计划是非常重要的一步。MySQL 提供了 EXPLAIN 语句来查看查询的执行计划。通过 EXPLAIN,我们可以了解查询是如何执行的,并找出可能的性能瓶颈。

# 使用 EXPLAIN 命令

你可以通过在查询前加上 EXPLAIN 关键字来查看执行计划。例如:

EXPLAIN SELECT * FROM users WHERE id = 1;
1

# 执行计划的各个字段解释

执行计划返回的结果包含多个字段,每个字段都有其特定的含义。以下是常见字段的解释:

  1. id:查询的序列号,表示查询中执行 select 子句或操作表的顺序。
    • id相同时,执行顺序由上至下
    • id不同时,id值越大优先级越高,越先执行
  2. select_type:查询的类型,常见的有:
    • SIMPLE:简单的 SELECT 查询,不包含子查询或 UNION
    • PRIMARY:最外层的 SELECT
    • SUBQUERY:子查询中的第一个 SELECT
    • DERIVED:派生表(在FROM子句中的子查询)
    • UNION:UNION中第二个及后面的SELECT语句
    • UNION RESULT:UNION的结果
  3. table:当前查询操作的表名或别名。
  4. partitions:匹配的分区信息(如果查询是基于分区表)。
  5. type:访问类型,表示MySQL如何查找表中的行,性能从好到差依次是:
    • system:表只有一行记录
    • const:通过索引一次就找到了,用于主键或唯一索引查询
    • eq_ref:唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配
    • ref:非唯一性索引扫描,返回匹配某个单独值的所有行
    • range:只检索给定范围的行,使用一个索引来选择行
    • index:全索引扫描,遍历整个索引树
    • ALL:全表扫描,MySQL遍历全表来找到匹配的行
  6. possible_keys:查询可能使用的索引。
  7. key:实际使用的索引。如果为NULL,则没有使用索引。
  8. key_len:使用的索引的长度。在不损失精确性的情况下,长度越短越好。
  9. ref:显示索引的哪一列被使用了。
  10. rows:MySQL认为必须检查的行数(估算值)。
  11. filtered:表示返回结果的行数占需读取行数的百分比。
  12. Extra:包含不适合在其他列中显示但十分重要的额外信息:
    • Using index:使用覆盖索引
    • Using where:使用WHERE过滤
    • Using temporary:使用临时表
    • Using filesort:需要额外的排序操作
    • Using join buffer:使用连接缓冲
    • Impossible WHERE:WHERE子句永远为false

# 关键访问类型详解

访问类型(type字段)是执行计划中最重要的指标之一,它显示了MySQL如何查找所需数据。按照性能从好到差的顺序:

system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL
1

主要类型说明:

  1. system/const:

    • system:表中只有一行数据
    • const:基于主键或唯一索引的等值查询
  2. eq_ref:

    • 出现在多表连接中
    • 使用主键或唯一非空索引关联
  3. ref:

    • 使用非唯一索引或唯一索引的前缀匹配
    • 返回匹配某个单独值的记录行
  4. range:

    • 索引范围扫描
    • 常见于 <、>、BETWEEN、IN、LIKE 等操作
  5. index:

    • 全索引扫描
    • 相比ALL快,因为索引文件通常比数据文件小
  6. ALL:

    • 全表扫描
    • 性能最差,应尽量避免

# 优化建议

  1. 索引优化:

    • 在WHERE和JOIN条件中的列上建立适当的索引
    • 考虑使用复合索引,注意索引列的顺序
    • 避免过多索引,可能影响更新性能
  2. 查询优化:

    • 只查询需要的列,避免SELECT *
    • 使用LIMIT限制结果集大小
    • 避免使用SELECT DISTINCT,考虑用其他方式替代
  3. 表结构优化:

    • 选择合适的数据类型,尽量使用较小的数据类型
    • 适当分表分库,控制单表数据量
  4. 监控与维护:

    • 定期分析慢查询日志
    • 使用ANALYZE TABLE更新统计信息
    • 合理设置long_query_time参数

# 实际案例分析

考虑以下查询:

EXPLAIN SELECT * FROM orders o 
JOIN users u ON o.user_id = u.id 
WHERE o.order_date >= '2023-01-01' 
  AND o.status = 'completed';
1
2
3
4

示例执行计划分析:

id select_type table type possible_keys key rows Extra
1 SIMPLE o range idx_date idx_date 1000 Using where
1 SIMPLE u eq_ref PRIMARY PRIMARY 1 NULL

分析要点:

  1. 连接查询中orders表使用范围扫描
  2. users表通过主键关联,使用eq_ref访问类型
  3. 可以考虑创建复合索引(order_date, status)来优化性能

通过对执行计划的深入理解和分析,我们可以更好地优化查询性能,提高数据库效率。

# 配合慢查询日志定位问题:全量记录所有语句

EXPLAIN 分析的前提是你已经知道"哪条 SQL 有问题"。生产环境排查性能问题时,第一步往往是打开慢查询日志(slow log)找出可疑语句。默认的 long_query_time(通常 10 秒)只会记录明显超时的查询,但很多性能问题其实是"单条查询不算慢,但被高频调用",这类语句永远进不了默认的慢日志。

临时开启全量记录(把阈值降到 0,记录所有执行过的语句)用于短时间抓取分析:

mysql> SET GLOBAL long_query_time = 0;
Query OK, 0 rows affected (0.00 sec)

mysql> SET GLOBAL min_examined_row_limit = 1;
Query OK, 0 rows affected (0.00 sec)
1
2
3
4
5

long_query_time = 0 表示"任何耗时的查询都记";min_examined_row_limit = 1 进一步过滤掉那些扫描 0 行的空跑语句,避免日志被心跳/健康检查类查询淹没。

验证:

mysql> SHOW VARIABLES LIKE 'slow_query_log%';
mysql> SHOW VARIABLES LIKE 'long_query_time';
1
2

确认 slow_query_log 为 ON 且 long_query_time 已变为 0.000000,然后跑几条业务查询,tail -f 慢日志文件应该能立刻看到新记录。

坑:这是一把双刃剑——开启全量记录后,慢日志会以业务 QPS 的速度疯狂增长,磁盘 I/O 和磁盘空间都会明显上升,只应该临时开启、抓取几分钟到几十分钟数据后立刻改回原值,绝不能作为生产环境的常态配置。抓完数据用 mysqldumpslow 或 pt-query-digest 聚合分析,找到 TOP N 高频/高耗时语句后,再对照本文前面的 EXPLAIN 方法逐条排查执行计划。

#执行计划#SQL优化
上次更新: 7/29/2026

← MySQL 快速分析binlog定位问题 DBA常用SQL和命令整理备查→

最近更新
01
Nginx 运维知识地图:从配置基础到反向代理实战 原创
07-29
02
MySQL 运维知识地图:从入门配置到高可用排障 原创
07-29
03
单表数据同步方案选型:为什么不该用 mysqldump 做「实时同步」 原创
07-29
更多文章>
Theme by Vdoing
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式