MySQL慢查询优化

一、识别慢查询:开启慢查询日志

首先需要准确定位哪些 SQL 是 "慢查询",MySQL 通过慢查询日志记录执行时间超过阈值的 SQL。

1. 配置慢查询参数

通过my.cnf(或my.ini)配置:
# 开启慢查询日志(默认关闭)
slow_query_log = 1
# 慢查询日志文件路径
slow_query_log_file = /var/log/mysql/mysql-slow.log
# 执行时间阈值(单位:秒,默认10秒,建议设为1-2秒)
long_query_time = 1
# 记录未使用索引的查询(即使执行时间未达标)
log_queries_not_using_indexes = 1
配置后重启 MySQL 生效,也可通过 SQL 动态设置(重启后失效):
set global slow_query_log = 1;
set global long_query_time = 1;

二、分析慢查询:使用 Explain 工具

找到慢查询后,通过EXPLAIN分析 SQL 执行计划,定位性能瓶颈。

1. Explain 用法

在 SQL 前加EXPLAIN
EXPLAIN SELECT * FROM user WHERE age > 30 AND name LIKE '张%';

2. 关键字段解读

字段 含义与优化点
type 访问类型,性能从好到差:system > const > eq_ref > ref > range > index > ALL
目标:至少达到range,避免ALL(全表扫描)。
key 实际使用的索引,若为NULL则未使用索引,需优化索引。
rows 估计扫描的行数,值越小越好(需结合实际数据量判断)。
Extra 额外信息,常见关键值:
Using filesort:需优化排序(如加索引);
Using temporary:使用临时表(性能差,需避免);
Using index:覆盖索引(理想状态)。

三、优化核心方法

1. 索引优化(最关键)

索引是提升查询速度的核心,需避免 "无索引" 或 "索引失效"。
(1)添加合适的索引
  • 单字段索引:where 子句中频繁出现的字段(如agestatus)。
  • 联合索引:多条件查询时,按 "字段区分度高→低" 排序创建(遵循最左前缀原则)。
    例:WHERE a=1 AND b=2 AND c=3,联合索引应设为(a,b,c)
  • 覆盖索引:索引包含查询所需所有字段(避免回表查询)。
    例:SELECT id,name FROM user WHERE age=30,创建(age,name)索引可覆盖查询。
(2)避免索引失效
以下情况会导致索引失效,需特别注意:
  • 使用函数 / 运算:WHERE SUBSTR(name,1,1)='张'(可改为name LIKE '张%')。
  • 隐式类型转换:WHERE phone=13800138000(若phone是 varchar,需加引号)。
  • LIKE%开头:WHERE name LIKE '%张'(无法使用索引,可考虑全文索引)。
  • 使用NOT IN!=<>等操作(可能导致全表扫描)。
  • 联合索引不满足最左前缀:(a,b,c)索引,仅用b=2查询会失效。

2. SQL 语句优化

(1)简化查询范围
  • 避免SELECT *:只查询需要的字段(减少数据传输,利于覆盖索引)。
  • 限制返回行数:用LIMIT分页,避免一次性返回大量数据。
(2)优化关联查询
  • 小表驱动大表:JOIN时,将数据量小的表作为驱动表(减少外层循环次数)。
  • 避免跨库 / 跨表 join:尽量在同一库内关联,减少网络开销。
(3)优化子查询
子查询效率较低,可改为JOIN
-- 低效子查询
SELECT * FROM user WHERE id IN (SELECT user_id FROM order WHERE amount > 1000);

-- 优化为JOIN
SELECT u.* FROM user u JOIN order o ON u.id = o.user_id WHERE o.amount > 1000;
(4)优化分页查询
大偏移量分页(如LIMIT 100000, 10)会扫描大量数据,优化方案:
-- 低效:扫描前100010行
SELECT * FROM user ORDER BY id LIMIT 100000, 10;

-- 优化:利用索引定位起点(需id有序)
SELECT * FROM user WHERE id > (SELECT id FROM user ORDER BY id LIMIT 100000, 1) LIMIT 10;

3. 表结构优化

(1)选择合适的数据类型
  • 用更小的类型:TINYINT(1 字节)代替INT(4 字节)存储小范围数字。
  • 避免大字段:TEXT/BLOB等大字段单独分表(减少主表 IO)。
  • 日期用DATE/DATETIME:而非VARCHAR(便于排序和计算)。
(2)分表分库(数据量大时)
当单表数据量超过 1000 万行,查询性能会明显下降,需拆分:
  • 水平分表:按规则拆分数据(如按用户 ID 哈希、按时间范围),每个表结构相同。
    例:订单表order_2023order_2024按年份拆分。
  • 垂直分表:按字段冷热拆分,将高频访问字段和低频字段分开存储。
    例:用户表拆分为user_base(基本信息)和user_detail(详细信息)。

4. 数据库配置优化

通过调整 MySQL 配置提升性能(my.cnf):
  • 缓存优化innodb_buffer_pool_size设为服务器内存的 50%-70%(缓存数据和索引,减少磁盘 IO)。
  • 连接优化max_connections根据并发量调整(避免连接数不足),thread_cache_size缓存线程(减少创建线程开销)。
  • 日志优化:关闭不必要的日志(如 general log),慢查询日志仅在排查问题时开启。

四、日常维护

  • 定期执行ANALYZE TABLE:更新表统计信息,帮助 MySQL 生成更优执行计划。
  • 监控慢查询日志:通过工具(如 pt-query-digest)分析慢查询趋势,提前优化。
  • 避免大事务:长事务会占用锁资源,导致其他查询阻塞(拆分为小事务)。
通过以上步骤,可有效定位并解决 MySQL 慢查询问题,核心原则是:减少扫描行数、高效利用索引、优化数据访问方式
阅读剩余
THE END