当数据库查询变慢时,你首先会想到什么命令?是EXPLAIN,还是去查看慢日志?掌握正确的性能分析命令,是每位后端工程师和DBA的必修课。今天,我们就来盘点几个在MySQL日常开发和故障排查中必备的性能分析命令,它们将帮助你快速定位SQL瓶颈。

1. explain:洞察SQL执行计划
在MySQL调优中,EXPLAIN绝对是我们的首选工具。它的使用非常简单,只需在待分析的SQL语句前加上这个关键字,MySQL便会展示其预估的执行计划。
explain sql语句;
-- 例如:
explain select * from t_user;
执行后的结果通常如下:
mysql> explain select * from t_user;
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------+
| 1 | SIMPLE | t_user | NULL | ALL | NULL | NULL | NULL | NULL | 7 | 100.00 | NULL |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
面对这一堆字段,我们该如何解读?以下是关键字段的含义解析:
- id: 查询的序列号。如果是简单的单表查询,通常为1。如果存在子查询或嵌套查询,你会看到多个id,数字越大,执行优先级越高。
- select_type:
SIMPLE 代表这是一个不包含子查询或UNION的简单查询。
- table: 显示查询涉及的表名。
- type: 这是需要重点关注的字段之一。它显示了访问类型,从最优到最差大致是:
system > const > eq_ref > ref > range > index > ALL。上面例子中的 ALL 意味着“全表扫描”,在数据量大时是主要的性能瓶颈。
- possible_keys: MySQL认为可能适用于此查询的索引。例子中为
NULL,表示没有找到可用的索引。
- key: MySQL实际决定使用的索引。如果为
NULL,则表示未使用索引。
- rows: MySQL预估为了找到所需行而必须检查的行数。这是一个估算值,但值越大通常意味着查询成本越高。
- Extra: 包含MySQL解决查询的额外信息。例如
Using filesort(使用文件排序)、Using temporary(使用临时表)等都可能是性能问题的信号。
在日常分析中,我们不必纠结于每个字段,重点观察 type、key、rows 以及 Extra 字段,就足以判断一条SQL的大致性能表现。
2. analyze:修正索引统计信息
你是否遇到过MySQL“选错索引”的情况?这往往是由于索引的统计信息不准确,导致优化器对查询代价做出了错误预估。ANALYZE TABLE命令可以重新收集表的统计信息。
为什么会不准?
EXPLAIN中输出的rows(扫描行数)是一个基于统计信息的估计值。如果这个估计值与实际情况偏差很大,就可能导致优化器选择非最优的索引。
analyze table 表名;
-- 例如:
analyze table t_user;
执行后,如果返回状态为OK,则说明统计信息已更新。
mysql> analyze table t_user;
+-----------------+---------+----------+----------+
| Table | Op | Msg_type | Msg_text |
+-----------------+---------+----------+----------+
| testdb.t_user | analyze | status | OK |
+-----------------+---------+----------+----------+
1 row in set (0.04 sec)
执行完ANALYZE后,再次运行EXPLAIN,MySQL会基于最新的、更准确的统计信息重新选择索引,很多“索引莫名失效”的问题由此得以解决。
当然,MySQL选错索引也可能是因为SQL语句本身的问题,例如对索引字段做了函数操作、发生了隐式类型转换或字符编码转换等。
3. show index:查看表索引结构
进行性能分析时,了解表上到底建立了哪些索引是基础。SHOW INDEX命令可以清晰展示一张表的索引详情。
show index from 表名;
-- 例如:
show index from t_user;
命令输出包含大量信息,我们关注几个核心字段:
mysql> show index from t_user;
+--------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+--------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| t_user | 0 | PRIMARY | 1 | id | A | 7 | NULL | NULL | | BTREE | | | YES | NULL |
| t_user | 1 | idx_phone | 1 | phone | A | 7 | NULL | NULL | YES | BTREE | | | YES | NULL |
+--------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
- Key_name: 索引名称。
PRIMARY代表主键索引。
- Non_unique: 0表示唯一索引,1表示普通索引(允许重复值)。
- Column_name: 构成索引的列名。
- Seq_in_index: 该列在联合索引中的位置(从1开始)。这个顺序至关重要,它决定了联合索引是否能被最左前缀匹配规则命中。
- Cardinality: 基数,即该列中不重复值的预估数量。这个值越大,说明该列的区分度越高,建立索引的效果通常越好。如果基数非常低(例如“性别”字段),建索引的意义就不大。
- Index_type: 索引类型,InnoDB存储引擎默认都是
BTREE。
提示:Cardinality是一个采样估算值,并非精确值,ANALYZE TABLE可以更新它。
4. show processlist:实时监控数据库连接
当数据库突然变慢,感觉有SQL“卡住”时,第一时间就应该运行SHOW PROCESSLIST。它能帮你查看当前数据库中的所有连接及其正在执行的操作。
show processlist;
输出结果如下,我们可以快速定位异常:
mysql> show processlist;
+----+-----------------+------------------+----------+---------+------+------------------------+------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+-----------------+------------------+----------+---------+------+------------------------+------------------+
| 5 | event_scheduler | localhost | NULL | Daemon | 3508 | Waiting on empty queue | NULL |
| 9 | root | 127.0.0.1:43610 | testdb | Query | 0 | init | show processlist |
| 10 | root | 172.17.0.1:52642 | NULL | Sleep | 3205 | | NULL |
+----+-----------------+------------------+----------+---------+------+------------------------+------------------+
关键字段解读:
- Id: 连接的标识符。如果想终止某个连接,可以使用
KILL Id 命令。
- User/Host: 连接使用的用户名和客户端地址,有助于追踪问题来源。
- Command: 当前连接执行的命令类型,常见的有
Query(正在执行查询)、Sleep(连接空闲)。
- Time: 这是需要重点关注的字段。表示该命令已经执行了多长时间(秒)。如果某条SQL的
Time值异常大,说明它可能已经运行了很长时间,需要重点审查。
- State: 连接当前状态,例如
Sending data、Locked等。如果看到大量Locked,可能意味着存在锁等待问题。
- Info: 正在执行的SQL语句(可能会被截断)。想看完整语句可以使用
SHOW FULL PROCESSLIST。
5. 慢查询日志 (Slow Log):捕获系统所有慢SQL
前面的命令需要我们主动去排查特定的SQL。若要系统性地、被动地捕获所有执行缓慢的SQL,就必须依赖慢查询日志。
第一步:查看当前慢查询配置
show variables like '%slow_query%';
show variables like 'long_query_time';
输出示例:
mysql> show variables like '%slow_query%';
+---------------------+--------------------------------------+
| Variable_name | Value |
+---------------------+--------------------------------------+
| slow_query_log | OFF |
| slow_query_log_file | /var/lib/mysql/2be4983ea86a-slow.log |
+---------------------+--------------------------------------+
mysql> show variables like 'long_query_time';
+-----------------+-----------+
| Variable_name | Value |
+-----------------+-----------+
| long_query_time | 10.000000 |
+-----------------+-----------+
slow_query_log: 是否开启慢查询日志(ON/OFF)。
slow_query_log_file: 慢查询日志文件的存储路径。
long_query_time: 定义“慢”的阈值,执行时间超过此值(秒)的SQL会被记录。默认10秒,生产环境通常调整为1-2秒。对于测试,可以临时设置为0秒,以观察所有SQL的详细执行情况。
第二步:临时开启慢查询日志(重启后失效)
set global slow_query_log = 'ON';
set global long_query_time = 0; -- 对新建立的连接生效
set session long_query_time = 0; -- 对当前会话立即生效
第三步:分析慢查询日志
开启后,执行一些SQL,日志文件(如/var/lib/mysql/2be4983ea86a-slow.log)中就会出现类似记录:
# Time: 2026-03-01T08:39:58.839308Z
# User@Host: root[root] @ [127.0.0.1] Id: 9
# Query_time: 0.000484 Lock_time: 0.000004 Rows_sent: 1 Rows_examined: 1
SET timestamp=1772354398;
select * from t_user where phone="13789097853";
日志中的关键信息:
Query_time: SQL实际执行时间。
Rows_examined: MySQL为了返回结果扫描了多少行。如果这个值远大于Rows_sent(实际返回行数),通常意味着索引使用不佳,做了大量无效的扫描。
当日志文件变得庞大后,手动分析效率低下。可以使用MySQL自带的 mysqldumpslow 工具进行聚合分析:
# 查看执行时间最长的前10条SQL
mysqldumpslow -s t -t 10 /var/lib/mysql/2be4983ea86a-slow.log
命令总结
为了方便回顾,我们将这几个核心命令的作用和使用场景总结如下:
| 命令 |
作用 |
典型使用场景 |
explain |
查看SQL执行计划 |
分析单条SQL是否走了索引,预估性能 |
analyze |
重新统计索引信息 |
当MySQL因统计信息不准而选错索引时 |
show index |
查看表的索引结构 |
确认索引是否建立、区分度如何、联合索引顺序 |
show processlist |
查看当前所有连接和SQL |
数据库响应变慢时,快速定位长时间运行的查询或锁等待 |
| 慢查询日志 |
记录执行超时的SQL |
长期监控,系统性发现和优化慢SQL |
掌握这五个命令,你就拥有了从实时诊断到长期监控的完整MySQL性能分析能力。实践是掌握它们的最好方式,下次遇到数据库性能问题,不妨从运行一条 EXPLAIN 或 SHOW PROCESSLIST 开始你的排查之旅吧。如果想深入学习更多数据库或系统架构相关的实战知识,欢迎来 云栈社区 与大家一起交流探讨。