先用 top -c 查系统 cpu 分布,确认是否 mysqld 真正占用高 cpu;若 wa 高则为 i/o 瓶颈,若 mysqld %cpu 低则排查其他进程;再用 pidstat 定位线程级热点;结合 processlist 和 performance_schema 分析活跃会话与慢 sql;通过 explain 判断索引缺失或执行计划退化;最后检查连接数、sleep 线程及 innodb 相关配置。

确认是不是 mysqld 真的在吃 CPU
别一看到监控告警就冲进 MySQL,先看清楚 CPU 是被谁占的。执行 top -c,重点看两行:%Cpu(s) 里的 us(用户态)和 wa(I/O wait);再按 P 排序,找 mysqld 进程的 %CPU 值。
如果 wa 占比高(比如 >20%),说明磁盘在拖后腿,不是 SQL 计算问题;如果 mysqld 的 %CPU 并不高,但系统整体 CPU 高,那可能是备份脚本、日志轮转、定时采集程序在抢资源。
更细粒度地看线程级分布,用:pidstat -t -p $(pgrep mysqld | head -1) 1
如果只有 1–2 个线程 CPU 接近 100%,大概率是单条慢 SQL;如果几百个线程平均都占 20%+,那就是并发或连接数压垮了调度器。
查当前活跃会话和可疑 SQL
登录 MySQL 后,优先跑这句:SELECT id, user, host, db, command, time, state, info FROM information_schema.processlist WHERE command != 'Sleep' AND time > 3 ORDER BY time DESC LIMIT 20;
重点关注:
• time 值大的(>5 秒),尤其是反复出现的相同 info
• state 是 Sending data、Sorting result、Creating sort index —— 这些状态背后往往藏着全表扫描或大临时表
• host 和 user 集中在某个应用 IP 或定时任务账号,能快速锁定来源模块
别只信 SHOW PROCESSLIST 快照,它不保留历史。如果问题一闪而过,得配合 performance_schema.events_statements_current 查最近执行过的语句(需提前开启相关消费者)。
判断是不是索引缺失或执行计划退化
对 processlist 里抓到的可疑 SQL,直接加 EXPLAIN 看执行计划:EXPLAIN SELECT ... FROM ... WHERE ...
关键看这几列:
• type:要是 ALL 或 index,基本就是全表/全索引扫描
• key:显示 NULL 就代表没走索引
• rows:预估扫描行数,上万甚至几十万就得警惕
• Extra:出现 Using filesort 或 Using temporary 是性能红灯
注意:EXPLAIN 结果依赖统计信息。如果表数据量近期突增,但没执行过 ANALYZE TABLE table_name;,优化器可能选错索引。别跳过这步。
别忽略连接数暴涨和 Sleep 线程堆积
CPU 高不一定来自“正在跑”的 SQL,也可能是连接管理本身在耗资源。检查:SHOW GLOBAL STATUS LIKE 'Threads_connected';SHOW GLOBAL STATUS LIKE 'Threads_running';
如果 Threads_connected 接近 max_connections(比如 90%+),而 Threads_running 只有 5–10,说明大量连接卡在 Sleep 状态。这时要查:
• 应用是否没正确释放连接(连接池配置不合理、异常未 close)
• wait_timeout 是否设得过大(如默认 28800 秒),导致空闲连接长期不释放
• 客户端是否频繁建连断连(比如短连接高频调用)
临时缓解可设:SET GLOBAL wait_timeout = 60;
但根本解法得从应用层收口——连接复用、合理配置连接池最大空闲时间、避免循环内开新连接。
真正棘手的情况是:Threads_running 持续 >100,但每条 SQL 看起来都不慢。这时候得怀疑配置项,比如 innodb_thread_concurrency 设得太低反而引发线程争抢,或者 tmp_table_size 太小导致本该内存完成的排序/分组全落磁盘临时表——这些细节容易被跳过,却直接影响 CPU 调度效率。











