怎么优化慢SQL

优化慢 SQL 是数据库性能调优的核心工作之一。以下是系统性、实用的 慢 SQL 优化方法论,适用于 MySQL(尤其是 InnoDB 引擎),也部分适用于其他关系型数据库。


🔍 一、定位慢 SQL

1. 开启慢查询日志(Slow Query Log)

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

-- 设置慢查询阈值(例如超过 1 秒记录)
SET GLOBAL long_query_time = 1;

-- 开启慢日志(临时)
SET GLOBAL slow_query_log = ON;

日志文件路径:/var/log/mysql/slow.log(Linux 默认)

建议:生产环境长期开启慢日志,配合 log_queries_not_using_indexes=ON 捕获无索引查询。

2. 使用 EXPLAIN 分析执行计划

复制代码
EXPLAIN SELECT * FROM users WHERE age > 30 ORDER BY name;

重点关注字段:

  • type:访问类型(ALL 表示全表扫描,最差;ref/range 较好;const 最佳)
  • key:实际使用的索引
  • rows:预估扫描行数(越小越好)
  • Extra:是否有 Using filesort(需排序)、Using temporary(建临时表)等性能杀手

💡 进阶:使用 EXPLAIN FORMAT=JSONEXPLAIN ANALYZE(MySQL 8.0+)获取更详细信息。


二、常见慢 SQL 原因 & 优化策略

1. 缺少合适索引

  • 现象type=ALLrows 很大
  • 优化
    • WHEREJOINORDER BYGROUP BY 涉及的字段加索引

    • 遵循最左前缀原则 (复合索引 (a,b,c) 可用于 a=1 AND b=2,但不能用于 b=2 单独查询)

    • 覆盖索引:索引包含所有查询字段,避免回表

      复制代码
      -- 查询只查 id 和 name,可建 (status, name) 索引实现覆盖
      SELECT id, name FROM users WHERE status = 1;

2. 索引失效

常见导致索引失效的操作:

  • 对字段使用函数或表达式:WHERE YEAR(create_time) = 2025
    → 改为范围查询:WHERE create_time BETWEEN '2025-01-01' AND '2025-12-31'
  • 隐式类型转换:WHERE user_id = '123'(user_id 是 INT)
  • 使用 !=NOT INLIKE '%xxx'(前导通配符)
  • OR 条件未全部命中索引(可用 UNION 替代)

3. 大量数据排序(Using filesort)

  • 原因ORDER BY 字段无索引,或与 WHERE 条件索引不匹配
  • 优化
    • ORDER BY 字段建索引(最好与 WHERE 条件组成联合索引)

    • 减少 SELECT *,只查必要字段(减少排序内存占用)

    • 分页深翻页问题:用「游标分页」替代 LIMIT 10000, 20

      复制代码
      -- 传统(慢):
      SELECT * FROM orders ORDER BY id LIMIT 10000, 20;
      
      -- 优化(快):
      SELECT * FROM orders WHERE id > 10000 ORDER BY id LIMIT 20;

4. 临时表(Using temporary)

  • 常见于GROUP BY + ORDER BY 不一致、子查询不当
  • 优化
    • 确保 GROUP BY 字段有索引
    • 避免在 SELECT 中使用非聚合字段(除非 ONLY_FULL_GROUP_BY 关闭)
    • 将复杂子查询改写为 JOIN

5. JOIN 性能差

  • 原则

    • 小表驱动大表(MySQL 通常自动优化)
    • 被驱动表(右表)必须有索引
    • 避免多表 JOIN(>3 张表慎用)
  • 示例

    复制代码
    -- orders 表大,users 表小 → users 驱动 orders
    SELECT * FROM users u
    JOIN orders o ON u.id = o.user_id
    WHERE u.status = 1;

    → 确保 orders.user_id 有索引!

6. 大分页(Deep Pagination)

  • 问题:LIMIT 1000000, 10 会扫描前 100 万行
  • 解决方案:
    • 记录上一页最大 ID,用 WHERE id > last_id LIMIT 10
    • 业务允许时,限制最大翻页深度(如只允许查前 100 页)

7. 锁竞争或事务过大

  • 长事务会阻塞其他操作,导致"看似慢查询"
  • 优化:
    • 缩短事务(及时 COMMIT)
    • 避免在事务中处理业务逻辑
    • 使用 SELECT ... FOR UPDATE 时确保有索引,否则锁全表!

三、高级优化手段

方法 说明
SQL 重写 将子查询改为 JOIN,拆分复杂查询
读写分离 读请求走从库,减轻主库压力
分库分表 数据量超千万级时考虑(如用户表按 user_id 分片)
缓存 高频查询结果缓存到 Redis(注意缓存一致性)
归档历史数据 删除或迁移冷数据,减少表大小
相关推荐
Nandeska7 小时前
10、MySQL8.0新增特性
数据库·mysql
未来之窗软件服务7 小时前
计算机等级考试—KTV 管理系统数据流图大题—东方仙盟练气期
数据库·计算机软考·仙盟创梦ide·东方仙盟
云草桑7 小时前
.net AI开发04 第八章 引入RAG知识库与文档管理核心能力及事件总线
数据库·人工智能·microsoft·c#·asp.net·.net·rag
diediedei8 小时前
机器学习模型部署:将模型转化为Web API
jvm·数据库·python
m0_561359678 小时前
使用Python自动收发邮件
jvm·数据库·python
天空属于哈夫克38 小时前
企业微信外部群运营升级:API 主动推送消息开发实战
java·数据库·mysql
naruto_lnq8 小时前
用Python批量处理Excel和CSV文件
jvm·数据库·python
星火开发设计8 小时前
共用体 union:节省内存的特殊数据类型
java·开发语言·数据库·c++·算法·内存
Genie cloud8 小时前
外贸独立站建站完整教程
服务器·数据库·云计算
2301_822365038 小时前
数据分析与科学计算
jvm·数据库·python