MySQL 性能优化的起点,往往是读懂执行计划 、定位慢 SQL 原因,再根据问题对应地进行优化。本文整理了执行计划的核心知识,以及在实际项目中可直接使用的 SQL 优化方法,为开发人员提供一套完整的调优思路。
一、执行计划分析(EXPLAIN)
1. 什么是执行计划?
简单来说就是知道我们这个sql到底干了什么,是怎么干的。
执行计划(Execution Plan)是 MySQL 查询优化器对 SQL 语句进行分析后,给出的具体执行方式 。
通过执行计划,我们能够了解到:
-
查询的表访问顺序
-
SQL 使用了哪些索引、哪些未使用
-
扫描了多少行数据
-
使用了什么连接方式
-
是否发生排序、临时表
-
查询大致的成本和效率
优化 SQL 的第一步,就是先读懂执行计划。
2. EXPLAIN 输出的 12 列含义
执行计划总共有 12 列核心字段,含义如下:
| 列名 | 含义 |
|---|---|
| id | SELECT 的执行顺序标识。id 越大优先级越高。id 相同则从上往下执行。 |
| select_type | 查询类型(普通查询、子查询、UNION 等)。 |
| table | 当前访问的表名。 |
| partitions | 匹配到的分区(未分区则为 NULL)。 |
| type | 表访问方式,性能从好到差依次为 system > const > eq_ref > ref > range > index > ALL。 |
| possible_keys | 可能会用到的索引。 |
| key | 实际使用的索引。 |
| key_len | 使用的索引长度(以字节为单位)。 |
| ref | 与索引比较的列或常量。 |
| rows | 估计需要扫描的行数。 |
| filtered | 条件过滤后剩余行数的百分比。 |
| Extra | 附加执行信息,例如排序、临时表等。 |
下面分析其中
3. select_type 常见类型
简单来说它告诉你这条 SELECT 在整条 SQL 里的"身份"。
-
SIMPLE:简单查询,不包含子查询或 UNION。
-
PRIMARY:最外层 SELECT。
-
SUBQUERY:子查询中的第一个 SELECT。
-
UNION:UNION 中的第二个及后续 SELECT。
-
DERIVED:FROM 子查询(会被物化成临时表)。
-
UNION RESULT:UNION 合并结果。
4. type(访问类型)
简单来说type 体现了表的访问方式,是效率的核心,越靠近左边越快,ALL 最慢。
访问方式从最优到最差依次为:
sql
system > const > eq_ref > ref > range > index > ALL
常见解释:
-
const:主键或唯一索引等值查询,只返回一行。
-
eq_ref:关联查询时被关联表的唯一匹配。
-
ref:普通索引等值查询,可能返回多行。
-
range:范围查询使用索引(BETWEEN、>、< 等)。
-
index:扫描整个索引树。
-
ALL:全表扫描,是最慢的方式。
5. Extra 信息
简单来说它告诉你 SQL 过程中有没有临时表、额外排序、是否覆盖索引等。
Extra 中的提示对判断是否需要优化非常关键:
-
Using filesort
排序无法使用索引,MySQL 需要额外排序(性能较差)。
-
Using temporary
使用了临时表,常见于 ORDER BY、GROUP BY。
-
Using index
使用了覆盖索引,不需要回表查询,性能好。
-
Using where
使用 WHERE 过滤,但不一定使用索引。
-
Using join buffer
关联查询时被驱动表未走索引,需要 join 缓存。
其中 Using filesort / Using temporary 大多数情况下意味着 SQL 仍有优化空间。
二、SQL 优化最佳实践
SQL 优化的核心流程是:
sql
慢 SQL 定位 → 慢 SQL 分析 → 问题原因 → 对应优化方案
1. 慢 SQL 定位
开启慢查询日志(slow query log),记录执行时间超过一定阈值的 SQL。
通过慢日志可以快速找到需要优化的语句。
2. 慢 SQL 分析方向
慢 SQL 常见原因:
-
数据量太大
-
并发量太高
-
索引缺失或使用不当
-
SQL 语句书写不合理
-
表结构设计问题
-
业务需求设计不合理
-
数据库服务器性能瓶颈
在优化之前,先判断 SQL 是由于逻辑问题、索引问题还是架构瓶颈导致。
三、索引优化实践
1. 控制索引数量
-
单表不建议超过 5 个索引。
索引越多,维护成本越高,优化器的选择成本也越高。
2. 联合索引的顺序
-
区分度最高的字段放在最前
-
最常作为查询条件的字段放前面
因为联合索引遵循 最左前缀法则。
3. where/order/group 的索引使用
如果 where、order by、group by 中包含多个字段,建议建立联合索引而不是多个单列索引。
例如:
sql
SELECT age, city, name
FROM user
WHERE age = 20 AND city = 'Beijing'
ORDER BY name;
最优索引是:
sql
(age, city, name)
因为 name 在联合索引中已经天然有序,可以避免 filesort。
四、SQL 语句优化
1. 不要使用 SELECT *
-
增加解析与网络开销
-
会导致无法使用覆盖索引
2. 多表 join:小表驱动大表
优先让较小的数据集作为驱动表 。
避免大表驱动小表造成巨大的 join buffer 负担。
3. union 替换 union all(视情况)
-
union 会去重,消耗更多 CPU
-
如果业务允许重复,优先使用 union all
五、表结构优化
1. 常一起使用的字段放在同一张表
避免频繁 join。
2. 合理使用反范式
为了性能允许冗余字段,例如把频繁 join 的字段冗余到主表。
3. 使用更小的数据类型
数字使用 INT
时间使用 DATETIME/TIMESTAMP 或整型时间戳
避免使用 VARCHAR 过大
避免 TEXT/BLOB(必要时拆分扩展表)
4. 尽量避免 NULL
由于:
-
需要额外的 NULL 标记位
-
影响索引存储与比较
-
聚合操作需要特殊处理
六、业务层优化
许多慢 SQL 根源不是 SQL 本身,而是业务需求问题:
-
分页查第 1000 页,本身不合理
-
某些报表可以提前计算而不是实时计算
-
热点数据应该缓存,而不是实时读库
优化往往需要业务参与,而不是仅靠开发。
七、架构层优化
1. 读写分离
将读和写拆到不同实例,提高并发能力。
2. 分库分表
降低单表数据量,提升查询效率,但维护成本更高。
3. 使用缓存(Redis)
热点数据、高频查询尽量缓存,减少数据库压力。
4. 使用分布式数据库(如 TiDB)
适用于海量数据、高并发场景。
总结
本文从执行计划分析到 SQL、索引、表结构、业务、架构多方面,总结了完整的 MySQL 优化体系。核心思想始终是:
-
先根据执行计划定位问题
-
再有针对性地从 SQL、索引、业务或架构层面进行改进