
线上接口响应变慢、数据库 CPU 升高时,多数情况是少数几条 SQL 拖垮了整体。本文给出一套可复现的排查流程:开启慢日志、定位最耗时的语句、用 EXPLAIN 分析执行计划,再针对索引失效的典型场景做修复。命令基于 MySQL 5.7/8.0,具体参数以所用版本官方文档为准。
先确认当前配置:
SHOW VARIABLES LIKE 'slow_query_log%';
SHOW VARIABLES LIKE 'long_query_time';
SHOW VARIABLES LIKE 'log_queries_not_using_indexes';动态开启(重启后失效,持久化需写入 my.cnf):
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;
SET GLOBAL log_queries_not_using_indexes = 'ON';注意:long_query_time 对新建连接生效,已有连接需要重连。log_queries_not_using_indexes 在小表上会产生大量噪音,排查结束后建议关闭。云数据库一般在控制台的参数设置和日志管理中配置,入口以控制台为准。
慢日志直接看很难找重点,用 mysqldumpslow 按总耗时排序:
# -s t 按总耗时排序,-t 10 取前 10 条
mysqldumpslow -s t -t 10 /var/lib/mysql/slow.log如果安装了 Percona Toolkit,pt-query-digest 能给出更完整的统计:
pt-query-digest /var/lib/mysql/slow.log | less重点关注三个指标:总耗时占比、执行次数、扫描行数与返回行数之比(Rows_examined / Rows_sent)。比值很大,说明在扫描大量无用数据。
EXPLAIN SELECT * FROM orders
WHERE user_id = 1001 AND status = 'PAID'
ORDER BY created_at DESC LIMIT 20;关键列的含义:
type:访问类型,从好到差大致为 const、eq_ref、ref、range、index、ALL。出现 ALL 即全表扫描。key:实际使用的索引,为 NULL 表示没走索引。rows:预估扫描行数。Extra:出现 Using filesort、Using temporary 需要警惕;Using index 表示覆盖索引,是好信号。MySQL 8.0.18 起可用 EXPLAIN ANALYZE 获取真实执行耗时,但它会真正执行语句,生产环境对写入类语句不要使用。
WHERE DATE(created_at) = '2026-10-01' 无法使用 created_at 索引,应改写为范围条件:WHERE created_at >= '2026-10-01' AND created_at < '2026-10-02'WHERE phone = 13800000000,会发生转换导致索引失效,应加引号。LIKE '%abc' 无法使用 B+ 树索引。LIMIT 1000000, 20 需要先扫描再丢弃前一百万行,可改为基于上次主键游标的查询:SELECT * FROM orders WHERE id > 1000000 ORDER BY id LIMIT 20;针对前面的例子,建立覆盖过滤和排序的联合索引:
ALTER TABLE orders ADD INDEX idx_user_status_created (user_id, status, created_at);再次 EXPLAIN,确认 key 命中新索引且 Using filesort 消失。大表加索引前建议:
long_query_time。mysqldumpslow 或 pt-query-digest 找出总耗时最高的语句。EXPLAIN 检查 type、key、rows、Extra。慢查询优化的核心是先定位再动手:用慢日志找到真正耗时的语句,用执行计划判断问题出在索引还是写法,改完再量化验证。避免凭感觉乱加索引。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。