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

相关推荐
来来走走几秒前
Flutter开发 网络请求
android·flutter
ayaya_mana1 小时前
Nginx性能优化与安全配置:打造高性能Web服务器
运维·nginx·安全·性能优化
白鹭5 小时前
MySQL(多表查询练习)
数据库·mysql
独行soc7 小时前
2025年渗透测试面试题总结-18(题目+回答)
android·python·科技·面试·职场和发展·渗透测试
雨白7 小时前
登录和授权:Cookie与Authorization Header机制详解
android
Frank_HarmonyOS8 小时前
【Android -- 多线程】Handler 消息机制
android
一条上岸小咸鱼9 小时前
Kotlin 基本数据类型(一):概述及分类
android·kotlin
没盐水菠萝9 小时前
Android - 动态切换桌面图标
android
AI 嗯啦9 小时前
SQL详细语法教程(三)mysql的函数知识
android·开发语言·数据库·python·sql·mysql
跨界混迹车辆网的Android工程师10 小时前
adb 发送广播
android