🔥 慢查询优化技巧
👉 评论区扣「慢查询」,免费领《慢查询优化避坑手册+EXPLAIN速查表》,干活直接用!
你是不是遇到过“系统卡到崩,查半天不知道哪条SQL拖慢了”?今天这份实操指南,按“找问题→分析问题→解决问题→验证效果”的流程,把慢查询优化拆成5步——哪怕你是刚接触数据库的新手,跟着做也能把慢SQL从“秒级”降到“毫秒级”!
💡 先互动: 你平时遇到慢查询,第一反应是加索引还是改SQL?评论区说说你的“踩坑/成功”经历!
🎯 适用场景(先确认你是不是这个“用户”)
| 维度 | 具体说明 |
|---|---|
| 读者对象 | 后端开发、DBA、需要自查性能问题的技术负责人 |
| 前提条件 | 有数据库SELECT权限,能访问服务器/管理工具(MySQL Workbench/Navicat等) |
| 核心目标 | 找到慢SQL → 分析执行逻辑 → 优化后降低响应时间 |
🚀 第一步:开启慢查询日志(5分钟找到“慢SQL嫌疑人”)
优化的第一步,是知道“到底哪条SQL慢”——数据库默认不记录慢查询,必须手动开启!
操作步骤(以MySQL为例,复制就能用)
- 登录数据库客户端(命令行/Navicat均可);
- 执行命令开启日志(分临时/永久两种方式):
✅ 永久开启(生产环境推荐):-- 临时开启(重启数据库失效,适合测试) SET GLOBAL slow_query_log = ON; -- 开启慢查询日志 SET GLOBAL long_query_time = 2; -- 定义“慢”:超过2秒的算慢查询(可按需调小,比如0.5) SET GLOBAL log_queries_not_using_indexes = ON; -- 记录没走索引的查询(隐患SQL)- 找到数据库配置文件(my.cnf/my.ini);
- 添加以下内容,重启数据库:
slow_query_log = ON slow_query_log_file = /var/lib/mysql/slow.log # 日志保存路径 long_query_time = 2 log_queries_not_using_indexes = ON
- 验证开启状态:
💡 预期结果:SHOW VARIABLES LIKE 'slow_query_log%'; SHOW VARIABLES LIKE 'long_query_time';slow_query_log显示ON,long_query_time显示2.000000。
🕵️ 第二步:分析慢查询日志(10分钟还原“案发现场”)
开启日志后,跑一段时间(建议覆盖业务高峰期,比如24小时),再分析日志里的“嫌疑人”。
操作步骤
- 找到日志文件位置:
SHOW VARIABLES LIKE 'slow_query_log_file'; - 查看日志(推荐用专用工具,避免海量日志看晕):
✅ 参数解释:# 用mysqldumpslow汇总分析,找最慢的10条SQL mysqldumpslow -s t -t 10 /var/lib/mysql/你的服务器名-slow.log-s t按查询时间排序,-t 10只显示前10条。
📝 重点看这4个关键信息(日志里的“破案线索”)
Query_time:SQL执行时间(越大越可疑);Rows_examined:扫描行数(扫描越多,越慢);Rows_sent:返回行数(返回越多,网络传输越慢);- SQL语句本身(重点看WHERE条件、JOIN表、是否用了*)。
🔬 第三步:用EXPLAIN给SQL做“CT扫描”(核心!分析执行逻辑)
找到慢SQL后,别盲目改!先看数据库“怎么执行这条SQL”——EXPLAIN是最核心的工具。
操作步骤
- 在慢SQL前加
EXPLAIN:-- 原慢SQL SELECT * FROM users WHERE nickname LIKE '%张三%' AND status = 1; -- 分析执行计划 EXPLAIN SELECT * FROM users WHERE nickname LIKE '%张三%' AND status = 1; - 重点解读输出结果(新手只看这5列,够用):
| 列名 | 核心解读 | 危险信号 |
|---|---|---|
type | 访问类型(性能从好到差): const > eq_ref > ref > range > index > ALL | 出现ALL(全表扫描)= 性能瓶颈 |
possible_keys | 理论上能用的索引 | 有值但key为NULL → 索引没用上 |
key | 实际用的索引 | NULL = 没走索引 |
rows | 预计扫描行数 | 数值越大,查询越慢 |
Extra | 附加信息 | 出现Using filesort/Using temporary = 必须优化 |
💡 新手速记: 只要看到type=ALL或Extra里有红色警告,这条SQL就是优化重点!
💊 第四步:对症下药(3种常见场景,直接抄解决方案)
根据EXPLAIN的结果,针对性优化,90%的慢查询都逃不出这3种场景:
场景A:索引问题(最常见!没走索引/索引加错)
✅ 症状: type=ALL 或 key=NULL;
✅ 解决方案: 创建合适的索引(别乱加,加错更慢):
-- 单字段索引(适合单条件查询)
CREATE INDEX idx_nickname ON users(nickname);
-- 联合索引(适合多条件查询,按“查询频率高→低”排序)
CREATE INDEX idx_nickname_status ON users(nickname, status);
⚠️ 避坑提示:
- LIKE以%开头(如
%张三)会导致索引失效,尽量用张三%; - 索引不是越多越好!多了会拖慢插入/更新速度,只给“高频查询、区分度大”的字段加。
场景B:SQL写法问题(有索引但没用上)
✅ 症状: possible_keys有值,key为NULL,或rows数值大;
✅ 解决方案: 改写SQL(3个高频优化点):
| 错误写法 | 正确写法 | 优化原因 |
|---|---|---|
WHERE DATE(create_time) = '2023-01-01' | WHERE create_time >= '2023-01-01' AND create_time < '2023-01-02' | 索引列上用函数,索引失效 |
WHERE phone = 123456(phone是varchar) | WHERE phone = '123456' | 隐式类型转换,索引失效 |
SELECT * FROM orders LIMIT 100000, 10 | SELECT * FROM orders WHERE id > 100000 LIMIT 10 | 深分页扫描行数多,用ID定位更快 |
场景C:返回数据量过大
✅ 症状: Rows_sent数值大,网络传输慢;
✅ 解决方案: 拒绝SELECT *,只查需要的字段:
-- 错误
SELECT * FROM article WHERE category_id = 1;
-- 正确
SELECT id, title, create_time FROM article WHERE category_id = 1;
✅ 第五步:验证+监控(闭环!确保优化有效)
改完别忘验证,还要防止问题反弹:
- 再次执行
EXPLAIN: 对比type是否变好、rows是否减少; - 实际执行SQL: 看执行时间是否从“秒级”降到“毫秒级”;
- 长期监控:
- 定期看慢查询日志,确认优化后的SQL不再出现;
- 搭建监控看板(如Prometheus+Grafana),实时告警慢查询。
📊 实战案例:从50秒到0.1秒(全程还原)
原始慢SQL(业务反馈:查某分类文章要50秒)
SELECT * FROM article
WHERE category_id = 5
ORDER BY create_time DESC
LIMIT 20;
分析过程
- 慢查询日志: 发现
Rows_examined=500万(全表扫描); - EXPLAIN结果:
type=ALL(全表扫描)rows=5000000Extra=Using filesort(文件排序,慢上加慢)
优化方案
-- 1. 加联合索引(覆盖查询+排序)
CREATE INDEX idx_category_create ON article(category_id, create_time);
-- 2. 改写SQL(只查必要字段)
SELECT id, title, create_time FROM article
WHERE category_id = 5
ORDER BY create_time DESC
LIMIT 20;
优化效果
| 指标 | 优化前 | 优化后 | 提升 |
|---|---|---|---|
| 执行时间 | 50秒 | 0.1秒 | 500倍 |
| 扫描行数 | 500万 | 20行 | 25万倍 |
| 索引使用 | 无 | idx_category_create | ✅ |
📋 总结速查表(Checklist,贴在工位上)
| 步骤 | 关键动作 | 危险信号 | 常用命令/工具 |
|---|---|---|---|
| 1. 开启记录 | 开启慢查询日志 | 日志为空(阈值设太高) | SET GLOBAL slow_query_log = ON; |
| 2. 捕获SQL | 用mysqldumpslow分析日志 | 扫描行数远大于返回行数 | mysqldumpslow -s t -t 10 [日志文件] |
| 3. 分析SQL | 执行EXPLAIN | type=ALL / Using filesort | EXPLAIN [慢SQL]; |
| 4. 优化改造 | 加索引/改SQL | 索引加错字段 / SQL改完更慢 | CREATE INDEX ... / 改写WHERE条件 |
| 5. 复盘验证 | 重新EXPLAIN+执行 | 扫描行数没降 / 时间没变 | 对比优化前后的EXPLAIN结果 |
🎯 核心心法(记牢!)
慢查询优化 = 先定位具体SQL + 用EXPLAIN看执行逻辑 + 针对性加索引/改SQL → 切忌: 没看执行计划就盲目加索引,越改越慢!
🚨 常见踩坑总结(新手必看)
- 索引加的越多越好? ❌ 错!每个索引都会拖慢INSERT/UPDATE,只加必要索引;
- EXPLAIN只执行一次就够? ❌ 错!数据分布变化会导致执行计划改变,定期复查;
- 只看执行时间不看扫描行数? ❌ 错!有时候扫描行数降了但时间没变,可能是服务器负载问题;
- 优化完就不管了? ❌ 错!新版本上线后要重新监控,防止SQL退化。
💬 互动时间
- 你优化过最慢的一条SQL,执行时间从多少降到了多少?
- 有没有遇到过“加了索引反而更慢”的情况?评论区聊聊原因!
✅ 收藏这篇: 把慢查询优化流程存起来,下次遇到卡库直接照做; ✅ 点赞+关注: 后续更《EXPLAIN进阶解读》《索引设计避坑指南》; ✅ 评论扣「慢查询」: 免费领《慢查询优化避坑手册+EXPLAIN速查表》,打印出来就能用!