mysql查询慢是什么原因_mysql性能瓶颈排查

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

查询没走索引,是最常见的慢原因

MySQL 会默认对

WHERE
ORDER BY
GROUP BY
字段尝试用索引加速,但稍不注意就失效。比如:
WHERE name LIKE '%张%'
(左模糊)、
WHERE status + 1 = 2
(字段参与计算)、
WHERE name = ? AND age > ? ORDER BY created_at
但索引是
(name, age)
而没包含
created_at
—— 这些都会导致排序或条件部分无法复用索引。

实操建议:

EXPLAIN
type
是否为
ALL
index
(全表/全索引扫描),
key
列是否为
NULL
避免在索引列上做函数操作,如
WHERE DATE(create_time) = '2024-01-01'
→ 改成
WHERE create_time >= '2024-01-01' AND create_time 
联合索引注意最左前缀原则,查询条件跳过首字段(如索引
(a,b,c)
,只查
b = ?
)就完全用不上

大表
JOIN
或子查询没加驱动表控制

MySQL 的

JOIN
是嵌套循环实现,驱动表(外层表)越小,整体扫描行数越少。如果优化器选错驱动表,比如拿千万级用户表去驱动几十万订单表,就会产生亿级中间结果。

实操建议:

STRAIGHT_JOIN
强制指定驱动表顺序,例如
SELECT ... FROM small_table STRAIGHT_JOIN big_table ON ...
子查询尽量转成
JOIN
,特别是
IN (SELECT ...)
类型,MySQL 5.6+ 虽有优化,但复杂条件下仍可能退化为 N+1 查询
检查
EXPLAIN
输出的
rows
列,两表
rows
相乘若远超预期结果集大小,大概率是驱动顺序或连接条件缺失索引

sort_buffer_size
join_buffer_size
设置过小

ORDER BY
JOIN
无法走索引完成排序/关联时,MySQL 会把数据读进内存 buffer 做归并或哈希。如果 buffer 不够,就会写临时文件到磁盘(
Using filesort
/
Using temporary
),I/O 开销剧增。

实操建议:

观察
SHOW STATUS LIKE 'Sort_merge_passes'
,值持续增长说明频繁落盘排序;
Created_tmp_disk_tables
高则代表磁盘临时表多
临时调大会话级变量:
SET sort_buffer_size = 4M;
(注意不是全局改,避免内存耗尽)
该类 buffer 是「每个查询独占」而非共用,设太大反而引发内存争抢,一般单次查询 2–8M 足够,勿盲目堆到 256M

锁等待和 MVCC 版本链过长

看似只读查询变慢,很可能是被写事务阻塞。InnoDB 的一致性读需遍历版本链(

rollptr
指向 undo log),若长期运行的事务未提交,后续所有快照读都要回溯更长的链,CPU 消耗明显上升。

实操建议:

查阻塞源:
SELECT * FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(NOW()) - TIME_TO_SEC(TRX_STARTED) > 60;
找出运行超 1 分钟的事务
配合
information_schema.INNODB_LOCK_WAITS
INNODB_LOCKS
(MySQL 5.7+ 已移除,改用
performance_schema.data_locks
)定位谁在等谁
业务中避免在事务里做 HTTP 调用、文件读写等不可控耗时操作;SELECT 语句也尽量加
FOR UPDATE
LOCK IN SHARE MODE
明确意图,别依赖隐式锁

慢查询背后往往是多个因素叠加,比如一个没索引的

JOIN
再遇上小
sort_buffer
,就会同时触发全表扫描、磁盘排序、长版本链三重惩罚。定位时别只盯
EXPLAIN
,得结合
slow_log
Query_time
Rows_examined
对比看——有时候扫了 500 万行但只花 0.2 秒,有时候扫 2 万行却卡 3 秒,后者才真正值得深挖。

相关推荐