MySQL查询耗时忽高忽低的四大主因:1.大量Sleep连接未释放;2.后台任务刷磁盘I/O冲击buffer pool;3.innodb_buffer_pool_size过小致缓存命中率低;4.统计信息过期导致执行计划误判。

查 SHOW PROCESSLIST 时发现大量 Sleep 连接占着不放
MySQL 查询耗时忽高忽低,第一反应不是慢查询本身,而是连接没及时释放。很多应用用完连接不 close(),或者连接池配置不合理,导致活跃连接数虚高,后续真实查询被迫排队等资源。
实操建议:
- 执行
SHOW PROCESSLIST,重点关注Command列为Sleep、Time值持续 > 60 秒的连接,记下ID和User - 结合应用日志确认这些连接是否对应某类定时任务(比如凌晨跑的报表同步),检查该任务是否用了长连接但没设
wait_timeout或没主动断开 - 临时验证:用
KILL [ID]干掉几个可疑Sleep连接,观察后续查询延迟是否回落——如果明显改善,说明是连接堆积抢了资源 - 别只调
wait_timeout,得同步检查应用层连接池的maxIdle/minIdle/removeAbandonedOnBorrow等参数,否则治标不治本
系统后台任务在刷磁盘 I/O,SELECT 被拖慢
MySQL 的 SELECT 本身不写盘,但若同时有 mysqldump、pt-online-schema-change、或业务侧的全表导出脚本在跑,它们会疯狂读取数据页,把 buffer pool 冲刷得七零八落,后续查询要重新加载热数据,自然变慢。
实操建议:
- 用
iostat -x 1看%util和await,如果磁盘忙且平均等待时间 > 20ms,基本可锁定 I/O 瓶颈 - 查
top -H或pidstat -d 1,找 CPU 或 I/O 占比高的进程,重点盯住mysqld自身线程之外的进程(比如python3导出脚本、perl备份任务) - 用
lsof -p [mysqld_pid]看 MySQL 打开了哪些文件,再对比ls -lh /proc/[pid]/fd/,确认是否有大文件被反复读(比如正在读一个 50GB 的 ibd 文件) - 关键点:这类后台任务尽量避开业务高峰,且加
--limit或--chunk-size控制单次读取量,别一把梭
innodb_buffer_pool_size 设置过小,缓存命中率暴跌
Buffer pool 不够用时,每次查询都要去磁盘捞数据,而磁盘响应时间波动远大于内存,直接导致查询耗时毛刺明显。这不是偶发问题,是资源水位长期不足的表现。
实操建议:
- 查当前命中率:
SHOW STATUS LIKE 'Innodb_buffer_pool_read%';,算(1 - Innodb_buffer_pool_reads / Innodb_buffer_pool_read_requests) * 100,低于 95% 就危险 - 确认实际可用内存:用
free -h看available,别只看total;innodb_buffer_pool_size建议设为可用内存的 50%~75%,但绝不能超过available - 改配置后必须重启 MySQL 生效,线上改要选窗口期;别信“动态调整”,
SET GLOBAL innodb_buffer_pool_size在 MySQL 5.7+ 虽支持,但仅限于倍数扩容(如从 2G → 4G),且仍需足够空闲内存,失败时不会报错只会静默忽略 - 改完别忘了观察
Innodb_buffer_pool_pages_total和Innodb_buffer_pool_pages_data是否接近,差太多说明没真正用起来
慢查询本身没索引,但波动来自统计信息过期
执行计划看似稳定,但 MySQL 优化器依赖的表统计信息(cardinality)如果长时间没更新,会导致它误判数据分布,偶尔选错索引或走全表扫描——这种“间歇性慢”最让人抓狂,因为 EXPLAIN 看不出异常。
实操建议:
- 查统计信息更新时间:
SELECT TABLE_NAME, UPDATE_TIME FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'your_db' AND TABLE_NAME = 'your_table';,如果UPDATE_TIME是几个月前,基本就是它了 - 手动触发更新:
ANALYZE TABLE your_table;,注意这会锁表(InnoDB 行锁,但对大表仍有影响),优先选低峰期 - 检查
innodb_stats_auto_recalc是否为ON,以及innodb_stats_persistent是否启用;若关了自动更新,又没定期ANALYZE,那统计信息就彻底“躺平”了 - 别只盯着单条 SQL,用
SELECT * FROM performance_schema.events_statements_summary_by_digest ORDER BY AVG_TIMER_WAIT DESC LIMIT 10;找平均耗时高且标准差大的语句,它们最可能受统计偏差影响
真正难搞的是多种因素叠加:比如备份脚本在刷 I/O 的同时,某个定时任务又建了新连接不释放,buffer pool 又刚好吃紧——这时候单看任一指标都正常,得交叉比对时间线。记录好每次波动发生的具体分钟级时间戳,再回查系统日志和 MySQL 慢日志,才能串起真相。


















