MySQL 执行计划(Execution Plan)是用于了解 SQL 查询在数据库中实际执行方式的工具。通过分析执行计划,可以诊断查询的性能瓶颈并进行优化。
在 MySQL 中,通过 EXPLAIN 或 EXPLAIN ANALYZE 查看查询的执行计划。以下是执行计划的详细解析:
1. EXPLAIN 查询执行计划
执行以下命令获取查询执行计划:
EXPLAIN SELECT * FROM table_name WHERE condition;
执行结果包含以下字段:
| 字段名 | 描述 |
|---|---|
| id | 查询中每个 SELECT 子句的标识符,表示执行顺序。 |
| select_type | 查询的类型(简单查询、联合查询、子查询等)。 |
| table | 查询涉及的表名。 |
| partitions | 查询中扫描的分区(如果使用分区表)。 |
| type | 访问类型,表示查询访问数据的方法。 |
| possible_keys | 查询可能使用的索引。 |
| key | 实际使用的索引。 |
| key_len | 索引的长度。 |
| ref | 索引中被使用的列或常量。 |
| rows | 预计需要读取的行数。 |
| filtered | 按过滤条件保留的行的百分比。 |
| Extra | 额外的信息,例如使用临时表或排序等。 |
2. 常见字段详解
(1) id
- 每个
id代表一个子查询或查询块。 - 数字越大,优先执行。
- 同一个
id的语句按顺序执行。
(2) select_type
描述当前查询的类型:
- SIMPLE:简单查询,没有子查询或联合查询。
- PRIMARY:主查询(最外层的 SELECT)。
- UNION:联合查询的第二个或后续 SELECT。
- SUBQUERY:子查询。
- DEPENDENT SUBQUERY:依赖于外部查询的子查询。
- DERIVED:派生表(子查询作为 FROM 的一部分)。
(3) type(访问类型)
访问类型表示 MySQL 检索数据的效率,从最优到最差排序如下:
- system:表仅有一行记录。
- const:表仅返回一行结果,通常用于主键或唯一索引的等值查询。
- eq_ref:通过索引匹配单行记录,常用于主键和唯一索引。
- ref:使用非唯一索引或前缀索引扫描匹配记录。
- range:索引范围扫描,常见于
BETWEEN、<、>、IN等条件。 - index:全索引扫描,扫描整个索引树而不是表数据。
- ALL:全表扫描,性能最差。
(4) possible_keys 和 key
- possible_keys:查询中可能使用的索引。
- key:查询实际使用的索引。如果为空,则未使用索引。
(5) rows
预计需要扫描的行数。行数越少,查询性能越高。
(6) Extra
额外的信息,包括查询优化的提示:
- Using index:覆盖索引查询,仅从索引中读取数据,不需要回表。
- Using where:查询过滤了数据,需要通过
WHERE条件筛选。 - Using temporary:使用临时表(通常因分组或排序)。
- Using filesort:未使用索引排序,可能导致性能下降。
3. EXPLAIN ANALYZE
EXPLAIN ANALYZE 不仅展示查询计划,还提供查询的实际执行时间和行数。示例如下:
EXPLAIN ANALYZE SELECT * FROM users WHERE id = 10;
结果包含以下额外信息:
- Actual Rows:查询实际返回的行数。
- Filtered:实际数据过滤比例。
- Execution Time:每个步骤的执行耗时。
4. 查询优化建议
-
使用索引:
- 对查询中频繁使用的字段添加索引。
- 避免全表扫描(ALL),尽量使用
type为ref或range的访问方式。
-
优化
WHERE条件:- 使用等值条件替代范围条件。
- 避免在索引字段上使用函数或表达式。
-
避免临时表和排序:
- 避免
Using temporary和Using filesort。 - 尽量使用覆盖索引(
Using index)。
- 避免
-
拆分复杂查询:
- 将多表关联、子查询分解为多个简单查询。
-
更新统计信息:
- 定期运行
ANALYZE TABLE和OPTIMIZE TABLE,确保优化器拥有最新的统计信息。
- 定期运行
-
检查慢查询日志:
- 启用慢查询日志,定位耗时较高的查询。
示例:执行计划分析
查询:
EXPLAIN SELECT * FROM orders WHERE user_id = 1001 AND order_date > '2025-01-01';
结果:
| id | select_type | table | type | possible_keys | key | key_len | rows | Extra |
|---|---|---|---|---|---|---|---|---|
| 1 | SIMPLE | orders | range | user_id_index | user_id_idx | 4 | 500 | Using where; Using index |
分析:
- type:
range,使用索引进行范围扫描。 - key:
user_id_idx索引被使用。 - Extra:
Using where表示使用了索引,但仍需过滤数据。 - 优化建议:该查询已使用索引,性能较好,无需优化。
执行计划的选择规则
MySQL 优化器会根据以下因素决定采用哪种扫描方式:
- 索引是否存在:没有索引的列只能通过全表扫描查询。
- 索引选择性:选择性越高的索引,优化器越倾向使用。(有时候优化器选择不对,就要选择force_index强制选择走某个索引)
- 查询条件的范围:范围越大,范围扫描的成本越高。
- 返回结果的数量:优化器会衡量扫描的开销和返回结果集的大小。
- 表的数据量:对于小表,全表扫描可能比索引扫描更快。