MySQL慢查询导致CPU飙高的完整指南

 更新时间:2026年06月10日 08:55:46   作者:YanDDDeat  
本文详细解析了MySQL CPU飙高问题的排查与优化过程,重点介绍了RedoLog和BufferPool配置优化、慢查询日志开启及索引优化等关键步骤,最终实现CPU稳定和响应速度大幅提升,需要的朋友可以参考下

记录第一次排查生成环境下,mysql CPU飚高100%占用,应用程序拖慢,记录,解决后占用降为正常。

一、现象:CPU 100%

前天收到使用QA语料构建系统同学反馈,语料系统响应缓慢,请求超时。用 top 一看,MySQL CPU 使用率持续 100%+,但进入容器执行 SHOW PROCESSLIST没有任何活跃查询,全是 Sleep 状态。有点莫名其妙。

top - 08:30:00 up 30 days, load average: 2.50, 2.30, 2.10
MiB Swap: 4096.0 total, 3964.7 free, 131.3 used. 6434.6 avail Mem

PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
1878724 lxd    20   0 2809352 560612  34364 S 102.3   6.9   4:45.44 mysqld
1788 root   20   0 1007448  38620   8908 T   9.6   0.5  7053:32 oneav

二、Redo Log 满了

既然表面看不到活跃查询,先看 InnoDB 内部状态:

SHOW ENGINE INNODB STATUS\G

关键输出:

LOG
---
Log capacity                 104857600
Log capacity used            104857600   ← 100% 满!
...
BUFFER POOL AND MEMORY
---
Buffer pool size   8192
Buffer pool hit rate 948 / 1000          ← 仅 94.8%(正常 >99%)
...
ROW OPERATIONS
---
175526.47 reads/s                        ← 异常高

分析:构建 MySQL 容器时没有配置 redo logbuffer pool 大小,全是默认值。

  • Redo Log 总容量仅 96MB(48MB × 2 个文件),已写满 100%
  • Buffer Pool 仅 128MB(8192 页 × 16KB),命中率仅 94.8%
  • 配置严重偏小,导致 InnoDB 频繁刷脏页释放日志空间,消耗大量 CPU

查看具体配置:

SHOW VARIABLES LIKE 'innodb_log_file_size';
+----------------------+----------+
| Variable_name        | Value    |
+----------------------+----------+
| innodb_log_file_size | 50331648 |   -- 48MB
+----------------------+----------+

SHOW VARIABLES LIKE 'innodb_log_files_in_group';
+---------------------------+-------+
| Variable_name             | Value |
+---------------------------+-------+
| innodb_log_files_in_group | 2     |
+---------------------------+-------+

三、第一次优化:调大 Redo Log 和 Buffer Pool

修改配置文件并重启容器:

[mysqld]
# Redo Log(核心问题)
innodb_log_file_size = 256M
innodb_log_files_in_group = 3     # 总容量 768MB
# Buffer Pool
innodb_buffer_pool_size = 2G
# 其他优化
innodb_flush_log_at_trx_commit = 2
innodb_flush_method = O_DIRECT

重启后,MySQL CPU 从 100%+ 降到了 50% 左右,但依然偏高,问题没有彻底解决。

四、第二次优化:开启慢查询日志

怀疑还有隐藏的慢查询,于是开启慢查询日志:

SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 2;
SET GLOBAL log_queries_not_using_indexes = ON;

-- 验证配置
SHOW VARIABLES LIKE 'slow_query_log%';
+---------------------+----------------------------------+
| Variable_name       | Value                            |
+---------------------+----------------------------------+
| slow_query_log      | ON                               |
| slow_query_log_file | /var/lib/mysql/slow.log          |
| long_query_time     | 2.000000                         |
+---------------------+----------------------------------+

慢查询日志记录

# Time: 2026-06-03T12:09:41.527275Z
# User@Host: swust[swust] @ [172.18.0.3] Id: 398
# Query_time: 4.782904  Lock_time: 0.000003 Rows_sent: 1  Rows_examined: 310516
SET timestamp=1780488576;
SELECT count(*) AS count_1 
FROM (SELECT ... 31个字段 ... FROM datasets WHERE ...) AS anon_1;

# Time: 2026-06-03T12:09:46.161278Z
... 相同 SQL,每 5 秒执行一次

分析:这是任务执行日志定时查询接口 暴露出的慢 SQL。

  • SQL 写法问题:外层 COUNT(*) 套了一个子查询,且子查询里 SELECT *
  • 缺少索引WHERE 条件涉及三个字段(user_id, current_stage, created_at),没有复合索引
  • 高频执行:每 5 秒一次,每次扫描 31 万行,导致数据库疲于奔命

五、解决方案

1. 添加复合索引

USE qa_gen;
CREATE INDEX idx_user_stage_time ON datasets(user_id, current_stage, created_at);

2. 优化 SQL 写法

-- 改造前(慢):先查全部字段再计数
SELECT COUNT(*) FROM (
    SELECT * FROM datasets 
    WHERE user_id = 11 
      AND current_stage = 'question_generate' 
      AND created_at >= '2026-06-01 11:46:14'
) AS t;

-- 改造后(快):直接 COUNT
SELECT COUNT(*) FROM datasets 
WHERE user_id = 11 
  AND current_stage = 'question_generate' 
  AND created_at >= '2026-06-01 11:46:14';

3. 应用层代码优化

# 不要这样做(慢)
count = session.query(func.count()).select_from(
    session.query(Dataset).filter(...).subquery()
).scalar()

# 应该这样做(快)
count = session.query(func.count(Dataset.id)).filter(
    Dataset.user_id == 11,
    Dataset.current_stage == 'question_generate',
    Dataset.created_at >= '2026-06-01 11:46:14'
).scalar()

六、优化效果对比

指标优化前优化后改善
扫描行数310,516 行~2,000 行减少 99%
查询时间5.08 秒0.05 秒快 100 倍
MySQL CPU48–56%<5%恢复正常
数据传输量62 MB/次<1 KB/次减少 99.9%

索引使用验证

EXPLAIN SELECT COUNT(*) FROM datasets 
WHERE user_id = 11 
  AND current_stage = 'question_generate' 
  AND created_at >= '2026-06-01 11:46:14'\G

-- 优化前:
-- type: ALL (全表扫描)
-- rows: 310516
-- Extra: Using where

-- 优化后:
-- type: ref (索引查找)
-- key: idx_user_stage_time (使用索引)
-- rows: 1847
-- Extra: Using index (覆盖索引)

七、总结与反思

  1. MySQL 默认 Redo Log 仅 96MB、Buffer Pool 128MB,生产环境必须调优。
  2. SHOW PROCESSLIST 只能看到"此刻"的快照:慢查询执行时间很短(5 秒),如果查看时刚好落在空闲期,就会看到全是 Sleep慢查询日志才是定位问题的关键。
  3. COUNT 不要套子查询:直接 COUNT(*) 即可,避免无谓的全表扫描和大量数据传输。
  4. 为高频查询条件建立复合索引,扫描行数从 31 万降到 2 千,性能提升 100 倍。
  5. 高频轮询接口:每 5 秒一次的定时任务,加上低效 SQL,会把数据库拖垮。可以考虑降低频率或改用增量查询。

最终,MySQL CPU 稳定在 5% 以下,接口响应从降到毫秒级。

以上就是MySQL慢查询导致CPU飙高的完整指南的详细内容,更多关于MySQL慢查询导致CPU飙高的资料请关注脚本之家其它相关文章!

相关文章

  • Mysql复合主键和联合主键的区别解析

    Mysql复合主键和联合主键的区别解析

    这篇文章主要介绍了Mysql复合主键和联合主键的区别,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-04-04
  • MySQL存粹问题面试准备总结大全

    MySQL存粹问题面试准备总结大全

    这篇文章主要介绍了MySQL存粹问题面试准备总结的相关资料,MySQL面试中常见问题,包括但不限于事务,索引,引擎,场景优化等常见问题,有助于面试前的准备,需要的朋友可以参考下
    2026-05-05
  • MySQL错误提示:sql_mode=only_full_group_by完美解决方案

    MySQL错误提示:sql_mode=only_full_group_by完美解决方案

    有时候遇到数据库重复数据,需要将数据进行分组,并取出其中一条来展示,这时就需要用到group by语句,下面这篇文章主要给大家介绍了关于MySQL错误提示:sql_mode=only_full_group_by的完美解决方案,需要的朋友可以参考下
    2022-10-10
  • MySQL 慢日志相关知识总结

    MySQL 慢日志相关知识总结

    慢日志在日常数据库运维中经常会用到,我们可以通过查看慢日志来获得效率较差的 SQL ,然后可以进行 SQL 优化。本篇文章我们一起来学习下慢日志相关知识。
    2021-05-05
  • MySQL Galera Cluster部署与使用

    MySQL Galera Cluster部署与使用

    MySQLGaleraCluster是一种高可用、同步复制的数据库解决方案,本文主要介绍了MySQL Galera Cluster 部署与使用,具有一定的参考价值,感兴趣的可以了解一下
    2025-03-03
  • Centos7.3下mysql5.7.18安装并修改初始密码的方法

    Centos7.3下mysql5.7.18安装并修改初始密码的方法

    这篇文章主要为大家详细介绍了Centos7.3下mysql5.7.18安装并修改初始密码的方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-06-06
  • 详解mysql 获取当前日期及格式化

    详解mysql 获取当前日期及格式化

    本篇文章主要介绍了mysql 获取当前日期及格式化,具有一定的参考价值,感兴趣的小伙伴们可以参考一下。
    2016-12-12
  • SQL行转列与列转行详情

    SQL行转列与列转行详情

    这篇文章主要介绍了SQL行转列与列转行,违章以SQL数据展开详细内容,具有一定的参考价值,需要的小伙伴可以参考一下,希望对你有所帮助
    2022-03-03
  • MYSQL部署主从同步超详细过程

    MYSQL部署主从同步超详细过程

    本文主要介绍了在红帽7.9系统上源码部署MySQL主从复制的流程,包括克隆主库、配置唯一ID、创建同步账户、锁表测试、初始化从库及验证同步状态的步骤,感兴趣的可以了解一下
    2025-09-09
  • MySQL备份与恢复之保证数据一致性(5)

    MySQL备份与恢复之保证数据一致性(5)

    这篇文章主要介绍了MySQL备份与恢复之保证数据一致性,感兴趣的小伙伴们可以参考一下
    2015-08-08

最新评论