先看top中us是否长期>70%,若高则说明MySQL密集计算,如解析小查询、无索引WHERE或内存临时表不足;sy高提示连接风暴致上下文切换,wa高表明IO等待;再结合vmstat的r和cs值、show processlist中的Sleep及Copying to tmp table on disk状态综合判断。

确认是不是用户态(us)在吃CPU
高并发小查询导致CPU飙高,第一件事不是改SQL或加索引,而是看top输出里的%Cpu(s)行中us值是否长期高于70%。如果是,说明MySQL正在密集做计算——比如解析大量简单SQL、频繁创建/销毁线程、执行无索引的WHERE、或反复扫描小结果集。这时候sy(系统态)如果也高,就要怀疑连接数爆炸引发上下文切换;wa高则说明IO拖慢了响应,CPU只是“干等”,优化方向完全不同。
用vmstat 1 5重点盯两个字段:r(运行队列长度)持续大于CPU核数,说明线程在排队抢CPU;cs(每秒上下文切换)超过2万,基本可断定是连接风暴——哪怕每条SQL只耗1ms,1万并发连接也能把CPU打满。
查show processlist里有没有大量Sleep或Copying to tmp table on disk
执行show processlist时,如果看到大量状态为Sleep但Time列动辄几百秒的连接,大概率是应用没正确复用连接,或者wait_timeout设得过大,空闲连接堆积。这些连接本身不干活,但会占用线程资源、消耗内存、干扰调度。
更危险的是大量Copying to tmp table on disk:说明tmp_table_size和max_heap_table_size太小,本该在内存做的GROUP BY、ORDER BY、DISTINCT被强制刷到磁盘临时表,每次都要读写IO+序列化反序列化,CPU和IO双吃紧。
- 检查当前值:
show variables like 'tmp_table_size';、show variables like 'max_heap_table_size'; - 若值低于64M(如默认16M),且业务常有分组排序,建议同步调到128M或256M(需确保物理内存足够)
- 注意:这两个值必须设成相等,否则以较小者为准
避免小查询被OR、IN、函数包裹导致索引失效
高并发下,哪怕一条毫秒级查询,只要走全表扫描,1000 QPS就能让CPU满载。常见陷阱包括:
-
WHERE status = 'active' OR type = 'vip':即使status和type都有索引,OR通常让优化器放弃使用索引 -
WHERE DATE(create_time) = '2026-09-29':对索引字段用函数,直接失效 -
WHERE user_id IN (1,2,3,...1000):参数过多时,MySQL可能放弃索引走全表
修复方式很直接:OR拆成UNION ALL;函数运算移到应用层处理;IN列表超50个值就分批查,或改用临时表JOIN。
控制连接数与连接生命周期
很多高并发小查询场景,真正瓶颈不在SQL本身,而在连接管理。一个HTTP请求建一个MySQL连接、用完不关,几秒内就能撑爆max_connections。这时show processlist里全是Sleep,但Threads_connected接近上限,新请求排队等连接,CPU却在空转处理无效上下文。
- 应用层务必启用连接池(如HikariCP、Druid),并设置合理
maxPoolSize(通常设为CPU核数×2~4) - MySQL端调低
wait_timeout(建议30~60秒),让空闲连接快速释放 - 禁用
skip-name-resolve,避免DNS反查拖慢连接建立 - 云RDS注意:有些厂商的“连接数配额”包含后台线程,实际可用连接比标称值少10%~20%
真正难啃的点往往藏在连接生命周期里——不是SQL写得不够快,而是连接建得太随便、放得太懒散。调参能缓解,但根子在应用怎么拿连接、用完怎么还。


















