MySQL进程CPU飙升到900%,怎么处理?

284 阅读6分钟

大家好,今天咱们来聊聊一个面试常见的问题——MySQL 进程 CPU 飙升900%。这种情况在生产环境中时有发生。今天,我就结合实际案例,给大家分享一下如何应对 MySQL 进程 CPU 飙升的问题。


一、MySQL 进程 CPU 飙升的常见场景

正常情况下,MySQL 的 CPU 使用率应该在一个合理的范围内,但如果出现以下情况,CPU 使用率可能会瞬间飙升到 900% 甚至更高:

  1. 高并发查询:当系统面临大量并发请求时,尤其是没有索引的查询,数据库需要消耗大量 CPU 资源来处理这些请求。
  2. 慢 SQL 查询:一些复杂的 SQL 语句,比如多表连接查询、大数据量的聚合操作,如果没有优化,很容易导致 CPU 飙升。
  3. 频繁的 DDL 操作:比如表结构变更、索引调整等操作,可能会对数据库性能产生较大影响。
  4. 慢日志记录:如果开启了慢日志,大量日志写入磁盘也会进一步拖慢数据库性能。

二、MySQL 进程 CPU 飙升的排查与解决

(一)定位问题

当发现 MySQL 进程 CPU 飙升时,第一步是快速定位问题。以下是常用的排查步骤:

  1. 使用 top 命令确认问题 打开终端,运行 top 命令,查看当前系统中 CPU 使用率较高的进程。如果发现 mysqld 进程的 CPU 使用率异常高,基本可以确定是 MySQL 本身的问题。

  2. 查看当前会话 登录到 MySQL 数据库,执行以下命令:

    SHOW PROCESSLIST;
    

这个命令会列出当前所有会话,包括正在执行的 SQL 语句、会话 ID、用户、主机等信息。重点关注那些状态为 SleepQuery 的会话,尤其是那些执行时间较长的 SQL 语句。

  1. 分析执行计划 找到可疑的 SQL 语句后,使用 EXPLAIN 命令查看其执行计划:

    EXPLAIN SELECT * FROM your_table WHERE your_condition;
    

    执行计划会告诉你 SQL 语句的执行效率,比如是否使用了索引、是否进行了全表扫描等。如果发现某个查询没有使用索引,或者扫描了大量数据,这就是问题所在。

  2. 检查索引 索引是数据库性能优化的关键。如果某个字段经常用于查询,但没有建立索引,那么查询效率会非常低。可以通过以下命令查看表的索引情况:

    SHOW INDEX FROM your_table;
    

    如果发现某个字段没有索引,或者索引设计不合理,就需要考虑添加或调整索引。


(二)解决问题

定位到问题后,接下来就是解决问题。以下是几种常见的解决方案:

  1. 优化 SQL 语句 如果发现某个 SQL 语句的执行效率很低,可以通过以下方式优化:

    • 确保查询语句使用了索引。
    • 避免在 WHERE 子句中使用函数,因为这会导致索引失效。
    • 减少查询返回的字段数量,避免使用 SELECT *
    • 对于复杂的查询,可以考虑拆分为多个简单的查询。
  2. 添加索引 如果某个字段经常用于查询,但没有索引,可以通过以下命令添加索引:

    ALTER TABLE your_table ADD INDEX idx_column (your_column);
    

    添加索引后,查询效率通常会有显著提升。

  3. 关闭慢日志 如果开启了慢日志,大量日志写入磁盘可能会拖慢数据库性能。可以通过以下命令关闭慢日志:

    SET GLOBAL slow_query_log = OFF;
    
  4. 限制连接数 如果发现大量并发连接导致 CPU 飙升,可以通过限制连接数来缓解问题。可以通过以下命令查看当前最大连接数:

    SHOW VARIABLES LIKE 'max_connections';
    

    如果需要调整连接数,可以修改配置文件或直接执行:

    SET GLOBAL max_connections = 100;
    
  5. 使用缓存 对于一些高频查询的数据,可以考虑使用缓存系统(如 Redis)来减轻数据库的压力。通过缓存,可以避免频繁查询数据库,从而降低 CPU 使用率。


三、真实案例分享

(一)MySQL 优化案例

之前我负责的一个项目中,就遇到了 MySQL 进程 CPU 飙升的问题。当时,开发同事编写的 SQL 语句导致线上 CPU 使用率飙升到 900% 以上。经过排查,发现问题出在一个高频查询的 SQL 语句上:

SELECT id FROM user WHERE user_code = 'xxxxx';

这个查询语句没有使用索引,导致每次查询都需要全表扫描。通过 SHOW PROCESSLIST 发现大量类似的查询语句处于 Query 状态。

接下来,我查看了 user 表的索引情况:

SHOW INDEX FROM user;

发现 user_code 字段没有索引。于是,我为该字段添加了索引:

ALTER TABLE user ADD INDEX idx_user_code (user_code);

添加索引后,查询效率显著提升,CPU 使用率也逐渐下降。

但问题并没有完全解决。经过进一步分析,发现系统开启了慢日志,大量查询语句超出了慢日志的阈值,导致日志频繁写入磁盘。关闭慢日志后,CPU 使用率基本稳定在 300% 左右。

最后,我将部分查询数据迁移到 Redis 缓存中,进一步降低了对 MySQL 的查询压力。经过优化,CPU 使用率最终稳定在 70%~80% 之间。

(二)Java 进程 CPU 飙升案例

除了 MySQL,Java 进程 CPU 飙升也是一个常见的问题。最近我负责的一个项目上线后,发现 Java 进程的 CPU 使用率飙升到 700%,导致服务器频繁宕机。

经过排查,发现问题出在一个线程的空循环上。通过 top -Hp 命令,我找到了占用 CPU 较高的线程,并通过 jstack 命令定位到了问题代码:

while (isRunning) {
    if (dataQueue.isEmpty()) {
        continue;
    }
    byte[] buffer = dataQueue.poll();
    // 处理逻辑
}

这段代码中,dataQueue 是一个阻塞队列,但如果队列为空,线程会不断空循环,导致 CPU 飙升。我将代码改为:

while (isRunning) {
    try {
        byte[] buffer = dataQueue.take();
        // 处理逻辑
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

修改后,线程会在队列为空时进入阻塞状态,避免了空循环,CPU 使用率也显著下降。


四、总结

MySQL 进程 CPU 飙升是一个比较复杂的问题,但通过合理的排查和优化,可以有效解决。以下是一些关键点:

  1. 快速定位问题:使用 top 命令确认问题进程,通过 SHOW PROCESSLIST 查看当前会话,分析执行计划,检查索引。
  2. 优化 SQL 语句:确保查询语句使用索引,避免全表扫描。
  3. 关闭慢日志:在高并发场景下,慢日志可能会拖慢数据库性能。
  4. 使用缓存:通过缓存减轻数据库压力,降低 CPU 使用率。
  5. 限制连接数:通过限制连接数避免过多并发请求。

希望这篇文章能给大家带来一些启发。

最后分享一份大彬精心整理的大厂面试手册,包含计算机基础、Java基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等高频面试题,非常实用,有小伙伴靠着这份手册拿过字节offer~

围观朋友⭕:dabinjava