MySQL 慢查询优化教程:开启慢查询日志、EXPLAIN 分析 SQL、索引优化,让数据库不再拖后腿


网站变慢了,数据库往往是最容易被忽略的原因。一条没走索引的 SQL,数据一多就慢到离谱,把整站拖垮。MySQL 慢查询优化是每个 DBA 和站长都必须掌握的技能:先找到慢在哪,再用 EXPLAIN 分析,最后通过加索引、改 SQL 来根治。这篇文章给你一套完整的排查和优化流程。

一、为什么 SQL 会慢

数据库慢查询常见原因:

  • 全表扫描:SQL 没走索引,逐行扫整张表
  • 索引失效:写了索引但查询方式没命中
  • 数据量过大:表越来越大,缺乏合理的索引和分页
  • 锁等待/并发:写操作互相阻塞
  • 配置不当:缓冲池太小、连接数不够等

优化的思路是:先定位慢查询,再看执行计划,针对性加索引或改 SQL。

二、第一步:开启慢查询日志

MySQL 的慢查询日志会记录执行超过指定时间的 SQL。先看看状态:

SHOW VARIABLES LIKE 'slow_query_log';
SHOW VARIABLES LIKE 'long_query_time';

开启并把阈值设成 1 秒:

SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;

也可以配置到 MySQL 配置文件 my.cnf 里永久生效:

[mysqld]
slow_query_log = 1
slow_query_log_file = /var/log/mysql/mysql-slow.log
long_query_time = 1

用户那类敏感的写 SQL 也可以记录下来便于分析但可能泄露内容,按需关。

三、查看慢查询日志

慢查询会记录到 slow_query_log_file 指定的文件,用工具查看:

mysqldumpslow -s t /var/log/mysql/mysql-slow.log | head -20

mysqldumpslow 是 MySQL 自带的分析工具,-s t 按执行时间排序,能看到最慢的几条 SQL 和它们的执行次数。

pt-query-digest 是更强大的分析工具(Percona Toolkit),能按查询汇总统计。

四、用 EXPLAIN 分析慢 SQL

拿到一条慢 SQL 后,用 EXPLAIN 看它的执行计划:

EXPLAIN SELECT * FROM orders WHERE user_id = 123 ORDER BY created_at DESC;

重点看这几列:

  • type:访问类型。最好的是 const/eq_ref;ref/range 不错;ALL 是全表扫描,最差,要重点盯
  • key:实际用到的索引。为 NULL 说明没走索引
  • rows:扫描的行数。越少越好,差距大说明能优化
  • Extra:出现 Using filesort(文件排序)、Using temporary(临时表)通常意味着可以优化

看到 ALL + rows 很大 + key 为 NULL,基本就是"没走索引的全表扫描",最需要处理。

五、第二步:加索引(最常见手段)

1. 给经常查询/排序/关联的字段建索引:

CREATE INDEX idx_user_id ON orders(user_id);

2. 联合索引(多条件查询,注意字段顺序):

CREATE INDEX idx_user_status ON orders(user_id, status);

联合索引遵循"最左前缀"原则,(user_id, status) 能同时加速 WHERE user_id=? AND status=?,但单独查 status 用不到该索引。

3. 覆盖索引:查询的字段都在索引里,连表都不用回,性能极好。适当让 SELECT 只挑需要的字段。

六、优化 SQL 写法

除了加索引,SQL 本身也能优化:

  • 避免 SELECT *:只取需要的列,减少回表和网络开销
  • 避免在索引列上用函数/运算:如 WHERE DATE(created_at)='2024-01-01' 会不走索引,改成范围查询 created_at >= '...' AND created_at < '...'
  • LIKE '%xx' 不会走索引,要避免前置通配符
  • 合理分页:深层分页 LIMIT 100000,20 很慢,可用"上一页最后一条 id"做条件,或用覆盖索引
  • JOIN 时关联字段要有索引

七、其他常见优化

1. 检查是否有大字段/无索引的外键: 关联查询里字段要有索引。

2. 查看当前正在执行的慢 SQL/锁:

SHOW PROCESSLIST;

看到大量 Sending data、长时间 Waiting for lock,说明有慢查询或锁等待。

3. 查询缓存说明: 老版本 MySQL 有查询缓存,8.0 已移除;新版更推荐配合 Redis 做应用层缓存(参考本站 Redis 教程)。

4. 缓冲池调优(InnoDB):

[mysqld]
innodb_buffer_pool_size = 1G   # 一般设为物理内存的 50%-70%

缓冲池越大,越多的数据和索引留在内存,减少磁盘 IO。

八、一个完整优化案例

问题 SQL:

SELECT * FROM orders WHERE status='pending'
    AND created_at>'2024-01-01' ORDER BY created_at DESC;

EXPLAIN 显示 type=ALL、rows=500000、key=NULL——全表扫了 50 万行。

优化:

CREATE INDEX idx_status_created ON orders(status, created_at);

再 EXPLAIN,type 变 range、rows 降到几千,key 命中联合索引,Using filesort 也消失了。同样数据下,查询从几秒降到几十毫秒。

九、常见问题

Q1:加了索引还是慢?

检查是不是 type=ALL 没走索引、联合索引字段顺序不对、或查询列上用了函数导致索引失效。用 EXPLAIN 重新确认 key。

Q2:索引越多越好吗?

不是。索引写入要开销、占磁盘,太多会拖慢 INSERT 和 UPDATE。给高频查询的字段建必要索引即可,不要盲目全建。

Q3:慢查询日志一直很大?

把阈值 long_query_time 调大(如 2-3 秒),只记录真正慢的;定期清理或按天切割日志(参考本站日志切割教程)。

Q4:一条 SQL 本来慢,突然更慢?

可能是数据量增长、索引失效、或锁竞争。先看慢查询日志,再结合 EXPLAIN 和 SHOW PROCESSLIST 排查。

Q5:还是搞不定怎么办?

把慢 SQL 和 EXPLAIN 结果整理出来,配合 SHOW PROFILE/performance_schema 深入分析;必要时拆分查询或改由 Redis 缓存承担热点读。

十、结语

MySQL 慢查询优化有一套标准流程:开慢日志找慢 SQL → EXPLAIN 看执行计划 → 加索引 + 改 SQL → 验证效果。核心是让 SQL 走索引、减少扫描行数、避免 filesort。再配合缓冲池调优和 Redis 缓存,数据库就不会再成为网站的瓶颈。结合本站 MySQL 基础、Redis、PHP 性能优化教程一起看,整套优化思路就全了。


手机扫码阅读

微信或手机浏览器扫一扫,随时随地随心阅读与分享

网站 SEO 优化教程:标题关键词、TDK设置、URL伪静态与收录,让搜索流量涨起来

Linux swap 交换分区详解:内存不够如何扩容、swap 文件创建与调优,低配 VPS 不卡顿

评 论
更换验证码