做网站的最怕两件事:服务器挂了和数据库慢了。服务器挂了至少你知道挂了,数据库慢是那种"用户以为网站坏了但其实没坏只是要等8秒"的状态,更难排查。以下是按照优先级排序的MySQL性能诊断流程。
第一步:先看是不是真的MySQL的问题
很多"MySQL慢"其实是网络慢、PHP-FPM进程跑满了、或者客户端自己在排序。先用这个命令确认:
SHOW FULL PROCESSLIST;
看State列。如果大量连接在"Sending data",那是MySQL真的在干活。如果在"Locked"或"Waiting for table metadata lock",那是锁的问题。如果在"Sleep"但数量很多,那是连接池没回收。如果大部分在NULL或空状态,问题可能不在MySQL,在前端或应用层。
第二步:找出具体的慢查询
开启慢查询日志:
SET GLOBAL slow_query_log = 1;
SET GLOBAL long_query_time = 1;
SET GLOBAL log_queries_not_using_indexes = 1;
然后看慢查询日志文件或用pt-query-digest(Percona Toolkit里的)做汇总。大多数人到这里就能定位到问题——通常是一条查询因为没有索引而全表扫描了几十万行。
我遇到过的一个真实案例:客户说"网站首页加载要6秒"。慢查询日志一开,首页有个SQL是SELECT * FROM articles WHERE status='published' ORDER BY created_at DESC LIMIT 10。看起来很正常对吧?但articles表有12万行,status字段没建索引,每次请求都要扫全表。加了个联合索引(status, created_at),首页SQL从6秒降到0.02秒。
第三步:检查索引——最重要的一条
EXPLAIN SELECT ... 是MySQL排错的神器。关注这几个字段:
- type:最好的是const/eq_ref/ref,range也可以。如果出现ALL(全表扫描),这个查询一定有问题。
- rows:预估扫描的行数。如果rows很大(几十万以上),一定有优化空间。
- Extra:如果出现"Using filesort"或"Using temporary",说明MySQL在做额外的排序或用临时表——这些操作在数据量大的时候会严重拖慢性能。
一个常见的误解:索引越多越好。实际上每个INSERT和UPDATE都要维护所有索引,索引多了写入性能会明显下降。原则是:为WHERE、JOIN、ORDER BY里出现的字段建索引,其他字段不加。一个表有七八个索引就要审视一下了。
第四步:看缓冲池命中率
SHOW STATUS LIKE 'Innodb_buffer_pool_read%';
计算命中率:Innodb_buffer_pool_read_requests / (Innodb_buffer_pool_read_requests + Innodb_buffer_pool_reads)。如果低于99%,说明缓冲池太小,MySQL频繁读磁盘。简单解决方法:调大innodb_buffer_pool_size(但不要超过物理内存的70%)。
第五步:看连接数和超时设置
SHOW STATUS LIKE 'Threads_connected';
SHOW VARIABLES LIKE 'max_connections';
如果当前连接数接近max_connections,新请求就会排队等待。常见原因是应用没有正确关闭连接(忘了mysql_close或连接池耗尽)。另外检查wait_timeout和interactive_timeout——默认28800秒(8小时),很多人不调这个,结果是空闲连接一直占着不释放。改成600(10分钟)就够了。
总结排查顺序
1. PROCESSLIST → 确认问题在不在MySQL
2. 慢查询日志 → 找出具体的慢SQL
3. EXPLAIN → 分析慢SQL的执行计划,加索引
4. 缓冲池命中率 → 调内存参数
5. 连接数 → 调连接池和超时参数
按这个顺序走,大部分MySQL变慢的问题在第三步就能定位到。另外说一句:MySQL 8.0的EXPLAIN ANALYZE比老版本的EXPLAIN好用得多——它不仅显示执行计划,还显示每一步实际花了多少时间。如果你的MySQL还在5.7版本,升级到8.0是2026年最值得做的性能优化。
还木有评论哦,快来抢沙发吧~