慢查询定位:从全量日志到精准诊断
数据库运维中,慢查询是系统性能瓶颈的首要原因。MySQL提供了慢查询日志(Slow Query Log)作为诊断工具,但默认配置的阈值(10秒)对生产环境而言过于宽松,大部分影响用户体验的查询耗时在500毫秒到2秒之间。
第一步:调整慢日志阈值。将long_query_time设为0.1秒(100毫秒),并开启未使用索引的查询记录:
-- 临时生效(重启后失效)
SET GLOBAL long_query_time = 0.1;
SET GLOBAL log_queries_not_using_indexes = ON;
SET GLOBAL slow_query_log = ON;
-- 永久生效(写入my.cnf)
[mysqld]
slow_query_log = 1
long_query_time = 0.1
log_queries_not_using_indexes = 1
slow_query_log_file = /var/log/mysql/slow.log
第二步:使用pt-query-digest分析慢日志。pt-query-digest是Percona Toolkit中的工具,分析能力远超mysqldumpslow:
# 按执行时间排序,取Top 20慢查询
pt-query-digest --limit 20 /var/log/mysql/slow.log
# 输出示例:
# Rank Query ID Response time Calls R/Call V/M
# ==== ============ ============== ====== ======= ====
# 1 0xA3B2C1... 1523.4435 62% 3281 0.4467 0.05
# 2 0xD4E5F6... 432.1001 18% 1205 0.3588 0.12
输出中的V/M指标反映查询时间的波动性,V/M大于0.1说明查询性能不稳定,可能受锁等待或数据分布影响。
EXPLAIN执行计划深度解读
定位到具体慢查询后,使用EXPLAIN分析其执行计划。重点关注以下字段:
type字段(访问类型,从优到差排序):
– system/const:单行匹配,最优
– eq_ref:唯一索引匹配,次优
– ref:非唯一索引匹配
– range:索引范围扫描
– index:全索引扫描
– ALL:全表扫描,最差
Extra字段中的关键信息:
– Using filesort:额外的排序操作,消耗大量CPU和临时空间
– Using temporary:使用了临时表,通常出现在GROUP BY和DISTINCT操作中
– Using index condition:索引下推(ICP),减少了回表次数,是正向优化
-- 典型慢查询分析
EXPLAIN SELECT o.order_id, o.amount, u.nickname
FROM orders o
JOIN users u ON o.user_id = u.id
WHERE o.create_time > "2026-07-01"
AND o.status = 2
ORDER BY o.create_time DESC
LIMIT 20;
SQL查询优化的六个核心策略
策略一:覆盖索引消除回表
当查询所需的所有字段都包含在索引中时,MySQL无需回表读取数据行,查询效率成倍提升:
-- 优化前:需要回表
SELECT order_id, amount, status FROM orders WHERE user_id = 100;
-- 优化后:创建覆盖索引
ALTER TABLE orders ADD INDEX idx_user_cover (user_id, order_id, amount, status);
-- EXPLAIN中Extra变为Using index即为覆盖索引
策略二:避免索引失效的常见写法
-- 错误:函数导致索引失效
SELECT * FROM orders WHERE DATE(create_time) = "2026-08-04";
-- 正确:范围查询走索引
SELECT * FROM orders
WHERE create_time >= "2026-08-04" AND create_time < "2026-08-05";
-- 错误:隐式类型转换导致索引失效
SELECT * FROM orders WHERE order_no = 12345;
-- 正确:类型一致
SELECT * FROM orders WHERE order_no = "12345";
策略三:分页查询优化
-- 优化前:offset=100000时极慢
SELECT * FROM orders ORDER BY id LIMIT 100000, 20;
-- 方案1:游标分页(推荐)
SELECT * FROM orders WHERE id > 100000 ORDER BY id LIMIT 20;
-- 方案2:延迟关联
SELECT o.* FROM orders o
JOIN (SELECT id FROM orders ORDER BY id LIMIT 100000, 20) t
ON o.id = t.id;
策略四:GROUP BY优化
-- 为GROUP BY创建索引
ALTER TABLE orders ADD INDEX idx_status_createtime (status, create_time);
-- 查询时分组顺序与索引顺序一致
SELECT status, COUNT(*) FROM orders GROUP BY status;
策略五:子查询改写为JOIN
-- 优化前:相关子查询
SELECT * FROM users
WHERE id IN (SELECT user_id FROM orders WHERE amount > 1000);
-- 优化后:JOIN改写
SELECT DISTINCT u.* FROM users u
INNER JOIN orders o ON u.id = o.user_id
WHERE o.amount > 1000;
策略六:批量操作替代逐行操作
-- 优化前:循环单条INSERT
INSERT INTO logs (content) VALUES ("log1");
-- 优化后:批量INSERT
INSERT INTO logs (content) VALUES ("log1"), ("log2"), ..., ("log1000");
数据库高可用架构下的性能调优注意事项
在主从架构中,慢查询优化需要区分主库和从库的不同侧重点。主库侧重写入优化(减少锁持有时间、控制事务大小),从库侧重读取优化(索引优化、查询改写)。读写分离时,确保读请求路由到从库,避免主库承担不必要的查询压力。
口袋网提醒,SQL查询优化不是一次性工作,而是一个持续迭代的过程。建立慢查询自动采集与告警机制,定期Review Top N慢查询,才能保障数据库性能长期稳定。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/mysql-xing-neng-diao-you-zhong-man-cha-xun-ding-wei-yu-sql/