MySQL 慢查询排查:从慢日志到索引优化
数据库变慢,往往不是突然发生的,而是从一条条慢查询开始累积的。接口超时、页面加载转圈、定时任务越跑越久,根因常常不在应用代码,而在数据库里某条没有走好索引的 SQL。本文结合日常运维的排查习惯,梳理一条从慢日志定位问题、到用 EXPLAIN 看清执行计划、再到索引优化落地的完整链路。
一、什么是慢查询
慢查询是指执行时间超过阈值的 SQL。MySQL 默认的阈值由 long_query_time 控制,默认 10 秒,对大多数业务来说太宽松了,生产上通常设为 1 秒甚至 0.5 秒。需要注意的是,慢的 SQL 不一定是写得差的 SQL,也可能是数据量增长后,原来够用的索引失效了,或者统计信息过期导致优化器选错了执行计划。
二、开启慢日志,先拿到证据
排查慢查询的第一步不是猜,而是看日志。先确认慢日志是否开启:
SHOW VARIABLES LIKE 'slow_query_log';
SHOW VARIABLES LIKE 'long_query_time';
如果没有开启,可以在线开启并指定日志文件:
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;
SET GLOBAL slow_query_log_file = '/var/log/mysql/mysql-slow.log';
建议把配置同步写进 my.cnf,否则重启后会失效。慢日志里每条记录都会给出查询时间、锁等待时间、扫描行数和返回行数,重点关注三类记录:执行时间最长的、出现频率最高的,以及扫描行数远大于返回行数的。
直接读原始日志比较费眼睛,可以用 mysqldumpslow 做聚合:
mysqldumpslow -s t -t 10 /var/log/mysql/mysql-slow.log
按总耗时排序后排在前面的,就是最值得先优化的 SQL。
三、用 EXPLAIN 看清执行计划
拿到可疑 SQL 后,在前面加上 EXPLAIN 执行,重点看四个字段。type 是访问类型,从好到差大致是 const、eq_ref、ref、range、index、ALL,出现 ALL 意味着全表扫描,要优先处理。key 是实际使用的索引,为空说明没有用上索引。rows 是优化器预估要扫描的行数,这个数字越大,代价越高。Extra 里出现 Using filesort 或 Using temporary,说明排序或分组需要额外开销,也值得留意。
EXPLAIN SELECT * FROM orders
WHERE user_id = 1001 AND status = 1
ORDER BY create_time DESC LIMIT 20;
如果 type 是 ALL,rows 是几十万,而结果只有 20 条,问题就很明确了:这条 SQL 在全表扫描后排序取前 20 条。
四、索引优化的几个实操要点
第一,给高频查询条件建索引。上面这条 SQL 可以建一个联合索引 (user_id, status, create_time),让过滤、排序都走索引,避免回表加排序。第二,遵循最左前缀原则,联合索引的列顺序要按查询中出现频率和区分度排列,等值条件在前,范围条件在后。第三,避免让索引失效的写法,比如对索引列做函数运算、用 LIKE '%xxx' 前缀模糊匹配,或者隐式类型转换,都会让优化器放弃索引。第四,索引不是越多越好,每个索引都会拖慢写入并占用空间,定期用 sys.schema_unused_indexes 之类的方式清理长期不用的索引。
五、容易踩的几个坑
一是只看慢日志里的单条耗时,忽略了高频短查询的累积伤害,一条 0.2 秒但每秒跑几百次的 SQL,总体伤害可能更大。二是上线前只在小数据量环境测试,索引在测试库够用,到生产几千万行数据就撑不住了。三是改了索引却不验证,优化后一定要重新跑 EXPLAIN 并对比执行耗时,确认 type 和 rows 真正改善了。四是把缓存当成万能药,缓存能缓解读压力,但治不好写放大和索引缺失,根因还是要回到 SQL 和索引上。
六、排查清单
日常遇到慢查询,可以按这个顺序走一遍:确认慢日志已开启且阈值合理;用 mysqldumpslow 找出耗时最高的几条 SQL;对每条 SQL 跑 EXPLAIN,记录 type、key、rows 和 Extra;根据执行计划补索引或改写 SQL;上线后复查执行耗时和慢日志,确认问题消失。把这个流程固定下来,大部分慢查询问题都能在半小时内定位到根因。
慢查询排查本质上是一个用数据说话的过程:日志给方向,执行计划给证据,索引优化给解法。养成先看日志再动手的习惯,就能少走很多弯路。
- 点赞
- 收藏
- 关注作者
评论(0)