首先需要准确定位哪些 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 慢查询问题,核心原则是:减少扫描行数、高效利用索引、优化数据访问方式。