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 性能。

相关推荐
leo_messi9425 分钟前
多线程(五) -- 并发工具(二) -- J.U.C并发包(八) -- CompletableFuture组合式异步编程
android·java·c语言
csdn_aspnet3 小时前
MySQL主从延迟根因诊断法,从网络、IO、SQL到参数,系统化定位高并发下的同步瓶颈
数据库·mysql·主从
Deryck_德瑞克4 小时前
【已解决】MySQL连接出错 1045 - Access denied for user ‘root‘@‘::1‘
android·mysql·adb
2501_915918414 小时前
iOS性能测试工具 Instruments、Keymob的使用方法 不局限 FPS
android·ios·小程序·https·uni-app·iphone·webview
.豆鲨包5 小时前
【Android】组件化搭建的一般流程
android
回到原点的码农5 小时前
Linux(CentOS)安装 MySQL
linux·mysql·centos
m0_491267005 小时前
Docker部署MySQL 多库自动备份(结构+数据完整,适配CentOS)
mysql·安全
高梦轩5 小时前
MySQL 故障排查与优化
数据库·mysql
吴声子夜歌5 小时前
Node.js——操作MySQL数据库
数据库·mysql·node.js
爱丽_5 小时前
MySQL 锁等待与死锁进阶:怎么看等待、怎么降冲突(工程化套路)
数据库·mysql