MySQL慢SQL优化全攻略:从诊断到调优

目录

慢SQL日志分析与诊断

开启慢查询日志

慢查询日志分析工具

慢SQL优化策略

[1. 避免SELECT * 查询](#1. 避免SELECT * 查询)

[2. 创建高效索引](#2. 创建高效索引)

索引选择原则

索引使用注意事项

[3. 使用EXPLAIN分析执行计划](#3. 使用EXPLAIN分析执行计划)

[4. 优化排序操作](#4. 优化排序操作)

[5. 解决深分页问题](#5. 解决深分页问题)

[6. 避免全表扫描](#6. 避免全表扫描)

[7. 优化JOIN操作](#7. 优化JOIN操作)

[8. 合理使用子查询](#8. 合理使用子查询)

高级优化技巧

[1. 使用覆盖索引](#1. 使用覆盖索引)

[2. 索引条件下推(ICP)](#2. 索引条件下推(ICP))

[3. 使用批处理减少交互](#3. 使用批处理减少交互)

[4. 合理使用临时表](#4. 合理使用临时表)

名词解释

总结


慢SQL日志分析与诊断

开启慢查询日志

sql 复制代码
-- 开启慢查询日志功能
SET GLOBAL slow_query_log = 'ON';

-- 设置慢SQL时间阈值(单位:秒),超过此时间的查询会被记录
SET GLOBAL long_query_time = 5;

-- 查看慢查询日志文件位置
SHOW GLOBAL VARIABLES LIKE 'slow_query_log_file';

-- 使用mysqldumpslow工具分析慢查询日志
-- 常用参数:-s排序方式(t按时间,c按次数),-t显示前N条
-- 示例:mysqldumpslow -s t -t 10 /var/logs/mysql/slow.log

慢查询日志分析工具

除了mysqldumpslow,还可以使用:

  1. ​​pt-query-digest​​(Percona Toolkit的一部分):更强大的日志分析工具

    bash 复制代码
    pt-query-digest /var/logs/mysql/slow.log
  2. ​​MySQL Workbench​​:图形化界面分析工具

慢SQL优化策略

1. 避免SELECT * 查询

sql 复制代码
-- 不推荐
SELECT * FROM users WHERE id = 1;

-- 推荐:只查询需要的列
SELECT id, username, email FROM users WHERE id = 1;

​​原因​​:

  • 增加网络I/O负担
  • 当表中有大字段(如TEXT/BLOB)时性能影响显著
  • 可能使覆盖索引失效(后面会解释)

2. 创建高效索引

索引选择原则
  • ​高区分度​:选择区分度高的列建索引(如用户ID比性别更适合)
  • ​常用查询条件​:为WHERE、JOIN、ORDER BY、GROUP BY中的列建索引
  • ​短字段优先​:整型字段比字符串字段更适合做索引
sql 复制代码
-- 创建单列索引
CREATE INDEX idx_username ON users(username);

-- 创建复合索引
CREATE INDEX idx_status_created ON orders(status, created_at);
索引使用注意事项
  • ​​最左匹配原则​​:复合索引必须从左到右使用

    sql 复制代码
    -- 对于INDEX(a, b, c)
    WHERE a = 1 AND b = 2 -- 使用索引
    WHERE b = 2 AND c = 3 -- 不使用索引(缺少a)
    WHERE a = 1 AND c = 3 -- 部分使用索引(只用a)
  • ​​避免索引失效​​:

    • 不要在索引列上使用函数:WHERE YEAR(create_time) = 2023
    • 避免隐式类型转换:WHERE user_id = '123'(user_id是整型)
    • 避免使用!=、NOT IN、IS NULL等操作符

3. 使用EXPLAIN分析执行计划

sql 复制代码
EXPLAIN SELECT * FROM users WHERE username = 'john';

​​关键指标解读​​:

  • ​type​:访问类型(从好到差:system > const > eq_ref > ref > range > index > ALL)
  • ​possible_keys​:可能使用的索引
  • ​key​:实际使用的索引
  • ​rows​:预估需要检查的行数
  • ​Extra​:额外信息(如Using filesort、Using temporary表示性能问题)

4. 优化排序操作

sql 复制代码
-- 不推荐:大数据集文件排序
SELECT * FROM products ORDER BY price DESC LIMIT 100;

-- 推荐:为排序字段添加索引
ALTER TABLE products ADD INDEX idx_price (price);
SELECT * FROM products ORDER BY price DESC LIMIT 100;

​​原理​​:B+树索引本身是有序的,利用索引可以避免内存排序(Using filesort)

5. 解决深分页问题

​​问题​ ​:LIMIT 10000, 20会先读取10020条记录,然后丢弃前10000条

​​优化方案​​:

  1. ​​使用覆盖索引+延迟关联​​

    sql 复制代码
    SELECT * FROM products JOIN (
      SELECT id FROM products 
      WHERE category_id = 5 
      ORDER BY created_at DESC 
      LIMIT 10000, 20
    ) AS tmp USING(id);
  2. ​​记录上次查询位置​​(适用于有序数据)

    sql 复制代码
    -- 第一页
    SELECT * FROM products ORDER BY id DESC LIMIT 20;
    
    -- 后续页(假设上一页最后一条记录的id是12345)
    SELECT * FROM products WHERE id < 12345 ORDER BY id DESC LIMIT 20;

6. 避免全表扫描

  • 为查询条件添加适当的索引
  • 避免在WHERE子句中对字段进行运算或使用函数
  • 使用合适的查询条件,避免过于宽泛的条件

7. 优化JOIN操作

  • 确保JOIN字段有索引
  • 小表驱动大表(MySQL优化器通常会自动处理)
  • 避免多表JOIN(超过3个表考虑反范式化设计)

8. 合理使用子查询

sql 复制代码
-- 不推荐:相关子查询
SELECT * FROM users WHERE id IN (
  SELECT user_id FROM orders WHERE amount > 1000
);

-- 推荐:改用JOIN
SELECT DISTINCT u.* FROM users u
JOIN orders o ON u.id = o.user_id
WHERE o.amount > 1000;

高级优化技巧

1. 使用覆盖索引

​​覆盖索引​​:查询的所有字段都包含在索引中,无需回表

sql 复制代码
-- 假设有INDEX(username, email)
SELECT username, email FROM users WHERE username = 'john';

​​优势​​:减少I/O操作,提高查询速度

2. 索引条件下推(ICP)

MySQL 5.6+特性,将WHERE条件推到存储引擎层过滤

sql 复制代码
-- 假设有INDEX(a, b)
SELECT * FROM table WHERE a = 1 AND b LIKE '%test%';

3. 使用批处理减少交互

sql 复制代码
-- 不推荐
INSERT INTO users(name) VALUES ('a');
INSERT INTO users(name) VALUES ('b');

-- 推荐
INSERT INTO users(name) VALUES ('a'), ('b');

4. 合理使用临时表

对于复杂查询,可以考虑使用临时表分步处理

sql 复制代码
CREATE TEMPORARY TABLE temp_orders
SELECT user_id, SUM(amount) AS total 
FROM orders 
GROUP BY user_id
HAVING total > 1000;

SELECT u.* FROM users u
JOIN temp_orders t ON u.id = t.user_id;

名词解释

  1. ​​B+树​​:MySQL索引的数据结构,特点是:

    • 所有数据都存储在叶子节点
    • 叶子节点通过指针连接,适合范围查询
    • 树的高度低,查询效率稳定
  2. ​​回表​​:当使用非主键索引查询时,先通过索引找到主键,再通过主键索引查找完整数据的过程

  3. ​​覆盖索引​​:查询的列都包含在索引中,无需回表

  4. ​​最左匹配原则​​:复合索引必须从左到右使用,不能跳过前面的列

  5. ​​深分页​​:当LIMIT offset很大时(如LIMIT 100000, 10),MySQL需要先读取offset+limit条记录,性能差

  6. ​​Using filesort​​:表示MySQL需要进行额外的排序操作,通常是因为没有使用索引排序

  7. ​​Using temporary​​:表示MySQL需要创建临时表来处理查询,常见于GROUP BY、ORDER BY等操作

总结

慢SQL优化是一个系统工程,需要:

  1. 通过慢查询日志定位问题SQL
  2. 使用EXPLAIN分析执行计划
  3. 针对性应用优化策略
  4. 持续监控优化效果

记住:索引不是越多越好,每个索引都会增加写操作的成本。好的数据库设计加上合理的索引策略,才能获得最佳性能。

相关推荐
yolo_guo35 分钟前
调试mysql延迟与libevent回调实际发送回复时机问题
c++·mysql·libevent
旺仔学长 哈哈41 分钟前
springboot钓鱼爱好者交流平台APP设计与实现
java·spring boot·mysql·充电桩管理系统
JosieBook2 小时前
【数据库】MySQL 实战精通系列 · 第6篇:InnoDB 存储引擎、日志与崩溃恢复
数据库·mysql
FYKJ_20105 小时前
django电影推荐系统55066-计算机课程设计、毕业设计
vue.js·spring boot·python·mysql·typescript·spark·django
这个DBA有点耶7 小时前
MySQL字符集与排序规则深入:索引失效的隐蔽场景与排查方法
数据库·mysql·代码规范
闲云自留地8 小时前
从下载到登录:MySQL 在两大系统上的完整安装教程
数据库·mysql
fengkai454511 小时前
十一、MySQL 第 1‑3 章
运维·数据库·mysql
Lancker11 小时前
记一次 Druid 连接 MySQL 报 Communications link failure 的排查过程
数据库·mysql·adb
自强的小白11 小时前
mybatis_plus的核心功能(条件构造器)
mysql
JosieBook12 小时前
【数据库】MySQL 实战精通系列 · 第11篇:Redis 缓存与 MySQL 一致性实战
数据库·mysql·缓存