偶发性SQL执行毛刺主因是资源瞬时争抢与非SQL层干扰,需排查Sleep连接堆积、磁盘I/O被抢占、InnoDB buffer pool命中率偏低及日志刷新阻塞四大问题。

偶发性SQL执行毛刺(比如某条查询平时 20ms,突然跳到 2s)不是慢查询日志能直接抓到的问题——它往往不进慢日志,但用户体感极差。核心得盯住「资源瞬时争抢」和「非SQL层干扰」。
查Sleep连接是否堆积抢资源
大量未释放的 Sleep 连接会占满 Threads_connected,后续真实查询被迫排队,造成毛刺。这不是SQL慢,是“等不到连接”。
- 执行
SHOW PROCESSLIST,重点筛Command = 'Sleep'且Time > 60的连接 - 结合应用日志确认这些连接是否来自定时任务(如每小时跑一次的报表同步),检查它是否用了长连接却没设
wait_timeout或没主动close() - 临时验证:用
KILL [ID]干掉几个高龄 Sleep 连接,观察毛刺是否回落——如果明显改善,说明是连接堆积导致的资源饥饿 - 别只调数据库端
wait_timeout,还得同步检查应用层连接池的maxIdle、removeAbandonedOnBorrow等参数,否则只是把问题从数据库推到应用内存里
看磁盘I/O是否被后台任务冲刷
SELECT 本身不写盘,但若此时有 mysqldump、pt-online-schema-change 或业务导出脚本在全表扫描,就会疯狂读取数据页,把 innodb_buffer_pool 冲得七零八落,后续查询被迫频繁回磁盘,响应时间自然毛刺。
- 用
iostat -x 1看%util和await:若磁盘忙且await > 20ms,基本锁定 I/O 瓶颈 - 用
pidstat -d 1找 I/O 占比高的进程,重点盯mysqld外的进程(比如python3 export.py) - 用
lsof -p [mysqld_pid]查 MySQL 当前打开的文件,再对比ls -lh /proc/[pid]/fd/,确认是否有大文件(如 50GB 的 ibd)被反复读 - 这类任务必须避开高峰,且加
--chunk-size或--limit控制单次读取量,不能一把梭
验InnoDB 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建议设为available的 50%~75%,但绝不能超 - 注意:buffer pool 不够时,毛刺常呈周期性(比如每 2 分钟一次),因为 LRU 链表反复淘汰又重载热数据
盯InnoDB日志刷新是否触发阻塞
INSERT/UPDATE 毛刺常见于日志写满强制 checkpoint 场景:当 innodb_log_file_size 过小,log buffer 写满后必须等 checkpoint 完成才能继续,造成周期性延迟(比如每 2 分钟一次明显卡顿)。
- 查
SHOW ENGINE INNODB STATUS中 LOG 段:Log sequence number和Last checkpoint at差距极小,说明日志快写满了 - 估算建议值:用
Innodb_os_log_written / 3600推算 1 小时写入量,innodb_log_file_size至少覆盖该量;常见设置为1G~4G,而非默认的48M - 调整需重启,且要先备份原日志文件;线上改前务必在从库或测试环境验证 checkpoint 频率变化
真正难排查的毛刺,往往藏在“看起来无关”的地方:一个没关的 Sleep 连接、一个凌晨跑的导出脚本、buffer pool 刚好卡在 94.8% 命中率、或者日志文件大小离临界点只差 200MB。这些点单独看都不致命,叠加起来就变成用户眼里的“系统抽风”。


















