三分钟讲透:MySQL CPU 500% 的排查与优化套路
大家好呀,我是小米,31岁,坐标在一个天天和 MySQL 打交道的技术部,平时喜欢折腾技术,也喜欢把踩过的坑讲成段子,分享给大家。
今天要聊的题目,是很多小伙伴在社招面试时可能被问到过的一个经典问题:
“如果 MySQL 数据库 CPU 飙升到 500%,你会怎么处理?”
当时我第一次被问到这个问题的时候,脑子里第一反应是:“完蛋,这服务器怕不是要冒烟了吧!”但是冷静下来想想,这类问题其实考察的并不是你背答案,而是你有没有处理线上问题的思路和能力。
那今天,就让我给大家拆解一下这个问题,同时用一个小故事的方式,把我在真实项目里踩过的坑分享给你们。
1.故事开场:凌晨两点的报警电话
事情发生在去年的某个深夜。
那天我刚准备进入美梦,突然手机叮的一声,一条报警短信弹出来:
告警:MySQL CPU 使用率 500%,请立即处理!
我整个人瞬间清醒。500%是个什么概念?我们机器是 8 核的,500% 就是 5 核被压榨得死死的,服务器分分钟可能宕机。
于是我立刻披上外套,打开电脑远程登录服务器。接下来,就是一场和“CPU 飙升”的较量。
2.第一步:到底是不是 MySQL 的锅?
当遇到 CPU 飙升时,第一件事不是慌,而是要先定位元凶。
我敲下了熟悉的命令:
果然,mysqld 进程赫然在列,占用了绝大多数 CPU。
这一步很关键!因为很多人一上来就觉得一定是数据库的锅,其实有时候可能是别的进程(比如日志进程、恶意脚本)抢资源。
所以总结一下第一步:
先用 top 等操作系统命令,确认是不是 mysqld 占用导致的。如果不是,那就去追查别的进程。如果是,那就进入下一步3.第二步:谁在数据库里捣乱?
既然是 MySQL 占用过高,那接下来要看看:是哪个 SQL 把 CPU 吃爆了。
这时候经典命令登场:
图片
屏幕上立刻刷出了几十条正在运行的 session。果然,有几个 SQL 正在跑,而且状态卡在 Sending data 上,执行了十几秒还没结束。
这就是罪魁祸首了。
那么该怎么办呢?
先确认 SQL 是谁的:比如是哪个应用、哪个功能触发的。分析 SQL 是否有问题:比如写得不合理、没用上索引。检查数据量:有时候 SQL 没错,但是单表太大,扫描量太多,也会吃 CPU。我当时就是先挑出几个明显异常的 session,果断执行:
图片
CPU 使用率果然立刻从 500% 掉到 200% 左右。这一刀下去,算是止血了,但问题还没真正解决。
4.第三步:SQL 优化与索引
止血之后,我开始分析那几个消耗高的 SQL。
拿其中一条来说,大概长这样:
图片
听起来没啥毛病对吧?但是当我一看执行计划,发现它居然在全表扫描!原因是 status != CLOSED 这种写法,让索引彻底失效了。
于是我立刻和同事沟通,把 SQL 改成:
图片
同时在 customer_id, status 上建了一个联合索引。优化后的 SQL 再跑,瞬间就从几十秒降到毫秒级。
那一刻我心里只想说:
索引就是数据库的灵魂,写 SQL 时不考虑索引,就等着 CPU 飙升吧。
5.第四步:别忘了连接数的陷阱
不过,CPU 飙升并不一定都是“某几条 SQL”造成的。
有一次,我遇到的情况是:
每条 SQL 都很正常,执行时间很短,但 CPU 一样冲到 400%-500%。
后来一查,原来是因为应用层突然有个 bug,疯狂建立数据库连接,短时间内涌入了几百个 session。
这种场景下,你 kill 再多线程也没用,因为新的连接会源源不断涌进来。
解决办法就不一样了:
限制数据库最大连接数,防止被冲垮。检查应用层逻辑,为什么会短时间内有这么多连接?是重试机制问题?还是连接池没配置好?我记得那次,我们在 MySQL 配置里调整了 max_connections,同时应用端修复了连接池的 bug,CPU 才彻底恢复稳定。
6.总结经验:处理 CPU 飙升的黄金流程
到这里,我已经把两个最典型的场景讲完了。那我们不妨把思路梳理成一个面试时能用的黄金流程:
1)确认 CPU 占用元凶
用 top 等命令确认是不是 mysqld 占用。2)查看数据库当前线程
用 show processlist 找出消耗资源的 SQL。3)SQL 优化与索引调整
检查执行计划,优化写法,补充索引。4)必要时立即止血
kill 高消耗线程,观察 CPU 是否下降。5)排查并发连接问题
如果是大量 session 导致,就要限制连接数,修复应用逻辑。6)长期方案
调整内存参数、优化表结构、做分库分表,甚至引入缓存。图片
7.面试答题套路
如果你在面试中被问到这个问题,可以用这样的思路来回答:
“当 MySQL CPU 飙升时,我会先用操作系统命令确认是不是数据库导致的,如果不是则排查其他进程。如果是 MySQL 的问题,会通过 show processlist 查看正在运行的 session,找出是否有大 SQL 或者执行异常的 SQL。如果有高消耗 SQL,会先 kill 线程止血,再分析执行计划,看是否缺少索引、SQL 写法是否合理。如果不是单条 SQL 消耗过高,而是连接数暴增导致的,我会和应用侧排查为何会有这么多连接,同时通过限制连接数来缓解压力。最后再根据情况做持久优化,比如加索引、改 SQL、调参数。”
这样一套逻辑下来,面试官肯定会觉得你对线上问题的处理有条理。
8.彩蛋:小米的踩坑教训
最后给大家分享一个小插曲:
我第一次遇到 CPU 飙升时,没想太多,直接一通乱 kill,把好几个正常业务的 SQL 也杀了,结果引发了线上小故障,被领导批评了一顿。
所以后来我学会了一个原则:
先观察,后下手;先止血,后优化。
杀线程只是临时措施,真正的解决方案永远是优化 SQL 和系统架构。
9.结语
好了,今天的分享就到这里啦。
如果你正在准备 Java 社招面试,这道题一定要烂熟于心,因为它不光考数据库功底,还考察你遇事冷静、思路清晰的能力。
希望大家下次被问到:“MySQL CPU 飙升到 500%怎么办?”,都能胸有成竹地答出来,不再心慌。