MySQL 慢查询优化:从定位到根治的完整思路

一、什么是慢查询?

慢查询通常指执行时间超过预期阈值的 SQL,比如:

  • 单次执行超过 1s
  • 在高并发场景下平均超过 100ms
  • 导致 CPU / IO 飙高、接口超时

在 MySQL 中,可以通过 慢查询日志​ 来捕获这些 SQL。

sql 复制代码
-- 查看是否开启
SHOW VARIABLES LIKE 'slow_query_log';

-- 设置阈值(单位:秒)
SET GLOBAL long_query_time = 1;

二、慢查询优化的标准流程(核心)

先定位 → 再分析 → 后优化 → 最后验证

1️⃣ 开启慢查询日志,找到"真凶"

不要凭感觉优化,要用数据说话:

  • 开启慢查询日志

  • 使用 mysqldumpslowpt-query-digest分析

  • 重点关注:

    • 执行次数多
    • 平均耗时高
    • 扫描行数大

2️⃣ 使用 EXPLAIN 分析执行计划

对可疑 SQL 执行:

ini 复制代码
EXPLAIN SELECT * FROM orders WHERE user_id = 123;

重点关注字段:

字段 关注点
type ALL(全表扫描)是大忌
key 实际使用的索引
rows 扫描行数,越小越好
Extra Using filesort / Using temporary 需警惕

目标

  • type 至少达到 range
  • key 不为 NULL
  • 避免 filesort 和临时表

3️⃣ 索引优化(最常见的手段)

✅ 建索引的原则

  • 高区分度字段(user_id、order_no)
  • WHERE / JOIN / ORDER BY 常用字段
  • 联合索引遵循最左前缀
  • 控制索引数量(写多读少表尤其注意)

❌ 常见索引失效写法

  • 对索引字段使用函数:DATE(create_time)
  • 隐式类型转换:phone = 13800000000
  • 左模糊:LIKE '%abc'
  • OR 条件中混合无索引字段
  • 不符合最左前缀

✅ 覆盖索引(进阶)

sql 复制代码
-- 索引 (user_id, status)
SELECT user_id, status FROM orders WHERE user_id = 123;

避免回表,显著提升性能。


4️⃣ SQL 改写优化

  • 避免 SELECT *
  • 大分页优化:
sql 复制代码
-- 避免
SELECT * FROM orders LIMIT 100000, 20;

-- 推荐
SELECT * FROM orders WHERE id > 100000 LIMIT 20;
  • 减少不必要的排序和分组
  • 拆分复杂 JOIN 和子查询

5️⃣ 表结构与业务层优化

  • 冷热数据分离(历史数据归档)
  • 读写分离(主写从读)
  • 适当冗余字段,减少 JOIN
  • 写多读少场景控制索引数量

三、加索引不是银弹(重要认知)

很多初学者容易陷入一个误区:

"查询慢?加索引!"

但现实情况是:

  • 索引会降低 INSERT / UPDATE / DELETE 性能
  • 索引占用磁盘空间
  • 索引过多会增加死锁概率
  • 返回大量数据时,索引效果有限

正确姿势

索引解决的是"扫描行数过多"的问题,不是所有慢查询的解药。


四、什么时候考虑分库分表?

当以下情况出现时,索引优化已接近极限:

  • 单表数据量超过千万级
  • 写入成为明显瓶颈
  • 硬件扩容成本过高

此时可以考虑:

  • 分库分表(ShardingSphere / MyCat)
  • 引入 ES / ClickHouse 处理复杂查询

⚠️ 注意

  • 分库分表会增加系统复杂度
  • 非分片键查询会退化为全表扫描
  • 应作为最后手段,而非首选方案

五、总结

慢查询优化不是一蹴而就的过程,而是一套组合拳:

慢查询日志定位 → EXPLAIN 分析 → 索引 + SQL 优化 → 表结构 / 架构调整

记住一句话:

索引是加速器,不是救命稻草;架构是解决扩展性问题的终极手段。


附:面试速记口诀

  • 慢查先看日志
  • EXPLAIN 看 type / key / rows
  • 索引失效避函数、避左模、避隐转
  • 覆盖索引少回表
  • 分页不用大 offset
  • 分表是最后一步

相关推荐
BS30813_vx9 分钟前
31754+基于 Spring Boot + Vue 的网络文学交流分享平台设计与实现
vue.js·spring boot·后端
蛋先生DX11 分钟前
内存安全翻车现场的罪魁祸首,根源就在这三步里?
c++·后端·编程语言
Rain的Java大神之路16 分钟前
手写一个Spring容器
java·后端·mysql·spring·面试
IT_陈寒20 分钟前
明明设了默认值,为什么我的JavaScript函数参数还是undefined?
前端·人工智能·后端
智购科技自动贩卖机44 分钟前
自动售货机嵌入式系统安全加固实践:从Go语言国密算法到硬件防拆的工程化落地
大数据·人工智能·后端·安全·golang·系统安全
烂蜻蜓1 小时前
Flask入门教程(附录):模板渲染API——Jinja2集成全解析
后端·python·flask
摇滚侠1 小时前
《Spring Boot 3:高级与架构设计》第 2 章 IOC容器的高级机制 Environment 阅读笔记 4
spring boot·笔记·后端
豆角焖肉1 小时前
Spring Boot入门实战:从零搭建第一个应用
java·spring boot·后端·自动配置
mldong3 小时前
Go 开发者也有自己的轻量工作流引擎了:go get 一行,5 分钟跑通一条审批流
后端·go
BingoGo9 小时前
PHP clone 之后,为什么改副本会影响原对象?
后端·php