mysql 性能优化之Explain讲解

EXPLAIN是 MySQL 中用于分析查询执行计划的重要工具,通过它可以查看查询如何使用索引、扫描数据的方式以及表连接顺序等信息,从而找出性能瓶颈。以下是关于EXPLAIN的详细介绍和实战指南:

1. EXPLAIN 基本用法

SELECTINSERTUPDATEDELETE语句前加上EXPLAIN关键字即可查看执行计划:

复制代码
EXPLAIN SELECT * FROM users WHERE age > 18;

2. 关键字段解析

EXPLAIN返回的结果包含多个字段,重点关注以下几个:

id
  • 查询的标识符,数值越大优先级越高,相同数值按顺序执行。
type
  • 数据访问类型 ,从最优到最差排序:
    • system/const:单条记录查询(主键或唯一索引)。
    • eq_ref :唯一索引扫描(如JOIN操作)。
    • ref:非唯一索引扫描。
    • range :范围扫描(如WHERE age > 18)。
    • index:全索引扫描(仅扫描索引树)。
    • ALL:全表扫描(性能最差)。
possible_keys
  • 可能使用的索引,但不一定实际使用。
key
  • 实际使用的索引,若为NULL则未使用索引。
key_len
  • 索引使用的字节数,用于评估索引的选择性。
rows
  • MySQL 估算的扫描行数,值越小越好。
Extra
  • 额外信息,常见值:
    • Using filesort:需额外排序(性能开销大)。
    • Using temporary :使用临时表(如GROUP BYORDER BY)。
    • Using index:覆盖索引(仅通过索引即可获取所有数据)。

3. 实战优化案例

案例 1:全表扫描优化

问题 SQL

复制代码
SELECT * FROM orders WHERE status = 'paid';

EXPLAIN 结果

复制代码
+----+-------------+--------+------+---------------+------+---------+------+--------+-------------+
| id | select_type | table  | type | possible_keys | key  | key_len | ref  | rows   | Extra       |
+----+-------------+--------+------+---------------+------+---------+------+--------+-------------+
| 1  | SIMPLE      | orders | ALL  | NULL          | NULL | NULL    | NULL | 100000 | Using where |
+----+-------------+--------+------+---------------+------+---------+------+--------+-------------+

分析type=ALL(全表扫描),key=NULL(未使用索引)。
优化

复制代码
ALTER TABLE orders ADD INDEX idx_status (status);

优化后 EXPLAIN

复制代码
+----+-------------+--------+------+---------------+----------+---------+-------+------+-------------+
| id | select_type | table  | type | possible_keys | key      | key_len | ref   | rows | Extra       |
+----+-------------+--------+------+---------------+----------+---------+-------+------+-------------+
| 1  | SIMPLE      | orders | ref  | idx_status    | idx_status | 152     | const | 500  | Using index |
+----+-------------+--------+------+---------------+----------+---------+-------+------+-------------+

结果type=ref(索引扫描),rows=500(扫描行数大幅减少),Using index(覆盖索引)。

案例 2:复合索引优化

问题 SQL

复制代码
SELECT user_id, amount FROM orders WHERE user_id = 100 AND status = 'paid';

EXPLAIN 结果

复制代码
+----+-------------+--------+------+---------------+----------+---------+-------+------+-------------+
| id | select_type | table  | type | possible_keys | key      | key_len | ref   | rows | Extra       |
+----+-------------+--------+------+---------------+----------+---------+-------+------+-------------+
| 1  | SIMPLE      | orders | ref  | idx_user      | idx_user | 4       | const | 1000 | Using where |
+----+-------------+--------+------+---------------+----------+---------+-------+------+-------------+

分析 :仅使用了user_id索引,未使用status条件。
优化:创建复合索引:

复制代码
ALTER TABLE orders ADD INDEX idx_user_status (user_id, status);

优化后 EXPLAIN

复制代码
+----+-------------+--------+------+-------------------+-------------------+---------+-------------+------+-------------+
| id | select_type | table  | type | possible_keys     | key               | key_len | ref         | rows | Extra       |
+----+-------------+--------+------+-------------------+-------------------+---------+-------------+------+-------------+
| 1  | SIMPLE      | orders | ref  | idx_user_status   | idx_user_status   | 156     | const,const | 50   | Using index |
+----+-------------+--------+------+-------------------+-------------------+---------+-------------+------+-------------+

结果rows=50(扫描行数进一步减少),Using index(覆盖索引)。

案例 3:消除Using filesort

问题 SQL

复制代码
SELECT * FROM products ORDER BY create_time LIMIT 10;

EXPLAIN 结果

复制代码
+----+-------------+----------+------+---------------+------+---------+------+--------+----------------+
| id | select_type | table    | type | possible_keys | key  | key_len | ref  | rows   | Extra          |
+----+-------------+----------+------+---------------+------+---------+------+--------+----------------+
| 1  | SIMPLE      | products | ALL  | NULL          | NULL | NULL    | NULL | 100000 | Using filesort |
+----+-------------+----------+------+---------------+------+---------+------+--------+----------------+

分析 :全表扫描后进行文件排序(Using filesort)。
优化:添加索引:

复制代码
ALTER TABLE products ADD INDEX idx_create_time (create_time);

优化后 EXPLAIN

复制代码
+----+-------------+----------+------+---------------+-------------------+---------+------+------+-------------+
| id | select_type | table    | type | possible_keys | key               | key_len | ref  | rows | Extra       |
+----+-------------+----------+------+---------------+-------------------+---------+------+------+-------------+
| 1  | SIMPLE      | products | index| NULL          | idx_create_time   | 5       | NULL | 10   | Using index |
+----+-------------+----------+------+---------------+-------------------+---------+------+------+-------------+

结果type=index(索引扫描),消除了Using filesort

4. 高级用法:EXPLAIN ANALYZE

MySQL 8.0+ 支持EXPLAIN ANALYZE,返回更详细的执行信息,包括实际扫描行数和时间:

复制代码
EXPLAIN ANALYZE SELECT * FROM users WHERE age > 18;

5. 优化建议

  1. 优先优化type字段 :尽量避免ALLindex类型,目标是ref或更优。
  2. 确保key字段非NULL:通过创建索引让查询使用索引。
  3. 消除Using filesortUsing temporary:通过合理索引避免额外排序和临时表。
  4. 利用覆盖索引 :让Extra字段出现Using index,减少回表操作。
  5. 复合索引顺序:将选择性高的字段放在前面(如唯一值多的字段)。

6. 常见误区

  • 索引越多越好:过多索引会增加写操作开销和内存占用。
  • 忽视复合索引顺序:不满足最左匹配原则会导致索引失效。
  • 过度依赖EXPLAIN估算rows是估算值,实际可能有偏差,需结合SHOW PROFILE等工具验证。

通过EXPLAIN深入分析查询执行计划,针对性地优化索引和查询语句,可以显著提升 MySQL 性能。

相关推荐
别致的影分身2 小时前
MySQL InnoDB 存储引擎
数据库·mysql
认真就输2 小时前
性能优化:两条SQL索引优化,CPU占用率从40%降至25%
性能优化
程序员JerrySUN4 小时前
Valgrind Memcheck 全解析教程:6个程序说明基础内存错误
android·java·linux·运维·开发语言·学习
apocelipes4 小时前
使用uint64_t批量比较短字符串
c语言·数据结构·c++·算法·性能优化·golang
青草地溪水旁4 小时前
`MYSQL`、`MYSQL_RES` 和 `MYSQL_FIELD`的含义与使用案例
数据库·mysql·c
程序猿小D4 小时前
基于SpringBoot+MyBatis+MySQL+VUE实现的医疗挂号管理系统(附源码+数据库+毕业论文+答辩PPT+项目部署视频教程+项目所需软件工具)
数据库·vue.js·spring boot·mysql·毕业设计·mybatis·医疗挂号管理系统
仰望星空的凡人5 小时前
【JS逆向基础】数据库之mysql
javascript·数据库·python·mysql
Kiri霧6 小时前
Kotlin集合与空值
android·开发语言·kotlin
哪里不会点哪里.7 小时前
EXPLAIN:你的SQL性能优化透视镜
sql·oracle·性能优化