mysql中分析慢查询日志进行索引优化

来源:这里教程网 时间:2026-02-28 20:46:26 作者:

怎么看慢查询日志里真正耗时的 SQL?

MySQL 的慢查询日志(

slow_query_log
)默认只记录执行时间超过
long_query_time
的语句,但光看耗时容易误判——比如一条
SELECT
花了 2.3 秒,可能是因为没走索引全表扫描,也可能只是返回了 50 万行结果导致网络或客户端卡顿。

关键要看日志里的两个字段:

Rows_examined
(扫描行数)和
Rows_sent
(返回行数)。如果前者远大于后者(比如
Rows_examined: 124892
Rows_sent: 10
),基本可以断定是索引缺失或失效。

确保日志开启并记录足够信息:
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 1;
SET GLOBAL log_queries_not_using_indexes = ON;
log_queries_not_using_indexes
很重要——它会把没走索引的查询也记进来,哪怕执行很快,这类语句在数据增长后极易变慢
mysqldumpslow
快速聚合分析,例如:
mysqldumpslow -s t -t 10 /var/lib/mysql/slow.log
(按总耗时排序,取前 10 条)

EXPLAIN 看懂执行计划的关键指标

EXPLAIN
不是“看看就行”,要盯住几个硬指标:

type
字段:优先级从高到低是
const
eq_ref
>
ref
>
range
>
index
>
ALL
。出现
ALL
就是全表扫描,必须优化
key
字段:显示实际使用的索引名。如果是
NULL
,说明没走索引(注意:也可能是用了索引但被优化器放弃,需结合
possible_keys
Extra
判断)
Extra
字段:警惕
Using filesort
(需要额外排序)和
Using temporary
(临时表),尤其是二者同时出现,往往意味着
ORDER BY
+
GROUP BY
没命中索引覆盖

示例:对

orders
表查最近 7 天未支付订单

EXPLAIN SELECT * FROM orders 
WHERE status = 'pending' AND created_at > NOW() - INTERVAL 7 DAY;

type
ALL
,且
key
NULL
,说明
status
created_at
缺少联合索引;建索引应按「等值条件在前、范围条件在后」原则:
INDEX(status, created_at)

联合索引顺序怎么排才不踩坑?

联合索引不是字段随便堆砌,顺序直接影响能否命中。核心规则就一条:等值查询字段放前面,范围查询字段放后面

错误示范:
INDEX(created_at, status)
——
WHERE status = 'pending' AND created_at > '2024-01-01'
只能用上
status
的等值部分,
created_at
的范围无法利用索引排序,
type
仍可能是
range
或更差
正确写法:
INDEX(status, created_at)
—— 等值
status
定位数据块,再在块内按
created_at
范围扫描,
type
可达
range
,且支持后续
ORDER BY created_at
如果还有
ORDER BY user_id
,且
user_id
是等值条件,可加到索引末尾:
INDEX(status, created_at, user_id)
;但若
user_id
是范围或
IN
列表,则不能继续延伸索引

哪些情况加了索引也没用?

不是所有字段都适合建索引,有些场景建了反而拖慢写入、浪费空间,甚至让优化器选错执行路径:

低区分度字段:比如
gender
(只有 'M'/'F'),
is_deleted
(99% 是 0),索引选择性太差,优化器大概率直接全表扫描
频繁更新的字段:每条
UPDATE
都要维护索引树,写入压力大时得权衡读写比
LIKE
左模糊:
WHERE name LIKE '%abc'
无法使用索引;右模糊
'abc%'
可以,但中间模糊
'%abc%'
不行(除非用全文索引或倒排)
隐式类型转换:
WHERE phone = 13800138000
phone
VARCHAR
),MySQL 会把字段转成数字比较,导致索引失效;应统一为字符串:
WHERE phone = '13800138000'

最常被忽略的一点:索引列参与计算或函数,比如

WHERE YEAR(created_at) = 2024
,哪怕
created_at
有索引也完全用不上。改写为范围查询:
WHERE created_at >= '2024-01-01' AND created_at  才能走索引。

相关推荐