首先需要准确定位哪些 SQL 是 "慢查询",MySQL 通过慢查询日志记录执行时间超过阈值的 SQL。
通过my.cnf(或my.ini)配置:
slow_query_log = 1
slow_query_log_file = /var/log/mysql/mysql-slow.log
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分析 SQL 执行计划,定位性能瓶颈。
在 SQL 前加EXPLAIN:
EXPLAIN SELECT * FROM user WHERE age > 30 AND name LIKE '张%';
索引是提升查询速度的核心,需避免 "无索引" 或 "索引失效"。
- 单字段索引:where 子句中频繁出现的字段(如
age、status)。
- 联合索引:多条件查询时,按 "字段区分度高→低" 排序创建(遵循最左前缀原则)。
例:WHERE a=1 AND b=2 AND c=3,联合索引应设为(a,b,c)。
- 覆盖索引:索引包含查询所需所有字段(避免回表查询)。
例:SELECT id,name FROM user WHERE age=30,创建(age,name)索引可覆盖查询。
以下情况会导致索引失效,需特别注意:
- 使用函数 / 运算:
WHERE SUBSTR(name,1,1)='张'(可改为name LIKE '张%')。
- 隐式类型转换:
WHERE phone=13800138000(若phone是 varchar,需加引号)。
LIKE以%开头:WHERE name LIKE '%张'(无法使用索引,可考虑全文索引)。
- 使用
NOT IN、!=、<>等操作(可能导致全表扫描)。
- 联合索引不满足最左前缀:
(a,b,c)索引,仅用b=2查询会失效。
- 避免
SELECT *:只查询需要的字段(减少数据传输,利于覆盖索引)。
- 限制返回行数:用
LIMIT分页,避免一次性返回大量数据。
- 小表驱动大表:
JOIN时,将数据量小的表作为驱动表(减少外层循环次数)。
- 避免跨库 / 跨表 join:尽量在同一库内关联,减少网络开销。
子查询效率较低,可改为JOIN:
SELECT * FROM user WHERE id IN (SELECT user_id FROM order WHERE amount > 1000);
SELECT u.* FROM user u JOIN order o ON u.id = o.user_id WHERE o.amount > 1000;
大偏移量分页(如LIMIT 100000, 10)会扫描大量数据,优化方案:
SELECT * FROM user ORDER BY id LIMIT 100000, 10;
SELECT * FROM user WHERE id > (SELECT id FROM user ORDER BY id LIMIT 100000, 1) LIMIT 10;
- 用更小的类型:
TINYINT(1 字节)代替INT(4 字节)存储小范围数字。
- 避免大字段:
TEXT/BLOB等大字段单独分表(减少主表 IO)。
- 日期用
DATE/DATETIME:而非VARCHAR(便于排序和计算)。
当单表数据量超过 1000 万行,查询性能会明显下降,需拆分:
- 水平分表:按规则拆分数据(如按用户 ID 哈希、按时间范围),每个表结构相同。
例:订单表order_2023、order_2024按年份拆分。
- 垂直分表:按字段冷热拆分,将高频访问字段和低频字段分开存储。
例:用户表拆分为user_base(基本信息)和user_detail(详细信息)。
通过调整 MySQL 配置提升性能(my.cnf):
- 缓存优化:
innodb_buffer_pool_size设为服务器内存的 50%-70%(缓存数据和索引,减少磁盘 IO)。
- 连接优化:
max_connections根据并发量调整(避免连接数不足),thread_cache_size缓存线程(减少创建线程开销)。
- 日志优化:关闭不必要的日志(如 general log),慢查询日志仅在排查问题时开启。
- 定期执行
ANALYZE TABLE:更新表统计信息,帮助 MySQL 生成更优执行计划。
- 监控慢查询日志:通过工具(如 pt-query-digest)分析慢查询趋势,提前优化。
- 避免大事务:长事务会占用锁资源,导致其他查询阻塞(拆分为小事务)。
通过以上步骤,可有效定位并解决 MySQL 慢查询问题,核心原则是:减少扫描行数、高效利用索引、优化数据访问方式。