MySQL 占用 CPU 过高问题定位及优化

CPU 占用过高常见现象在使用 MySQL 的过程中会遇到各种瓶颈问题,常见的是 IO 瓶颈,但是有时候会出现服务器 CPU 使用率超过 100%,应用页面访问慢,登录服务器负载很高 。而导致这个问题竟然是 MySQL 进程,按理说如果 MySQL 运行稳定,服务器的 CPU 资源并不会跑满,如果出现这个问题,初步可以断定,是 MySQL 实例中出现了问题 SQL 。
CPU 占用过高常见原因CPU 占用过高常见原因:

  • 服务器硬件问题
  • 内存溢出
  • 业务高并发
如果是业务高并发引起,可以理解为一种业务繁忙的状态,有可能业务猛增,有可能是定期或者临时的并发窗口:
  • 数据库对象设计不合理
  • 触发器导致
  • 表索引设计不合理
  • 数据库锁导致,如行锁冲突、行锁等待、锁超时、死锁等
  • 系统架构没有缓存中间件
  • 读写分离配置不合理
  • OLTP 系统承载了 OLAP 的业务需求
  • 未合理升级改造为集群环境
  • 未配置异构数据分析系统
  • MySQL 系统参数设置不合理
  • 问题 SQL 导致
SQL 问题导致 CPU 使用率过高是最常见的现象,比如 group by、order by、join 等,这些很大程度影响 SQL 执行效率,从而占用大量的系统资源 。
说了这么多常见原因,其实总结一句话来说就是现有系统的现有配置下的现有环境提供不了所需要的数据查询、分析、执行能力,针对这个问题,首先我们要发现问题的所在,就是说我们要准确的定位问题,然后针对问题进行优化,再考虑其他升级改造的事情 。
定位分析问题 SQL光说不练假把式,接下来我们比划比划 。
首先,搭建一套模拟环境,这里我让我的得力助手 DBdeployer 来帮我搭建一套模拟环境 。
DBdeployer 是用 Go 语言实现的一款非常强大与高效的部署数据库环境的开源工具,我们完全可以用它来部署开发、测试环境 。如果你对这个助手感兴趣,可以看我的另外一篇 Chat 。
MySQL 安装部署:我是如何“秒级”部署 MySQL 集群
使用 DBdeployer 来部署一个单点 5.7.27 版本的 MySQL,并配置一个模拟 CPU 占比高的测试环境 。
[root@localhost ~]# dbdeployer deploy single 5.7.27Database installed in /dbdata/sandboxes/msb_5_7_27run 'dbdeployer usage single' for basic instructions'.. sandbox server started创建测试表:
[root@localhost ~]# cd /dbdata/sandboxes/msb_5_7_27/[root@localhost msb_5_7_27]# ./use mysql [localhost:5727] {msandbox} ((none)) > use testDatabase changedmysql [localhost:5727] {msandbox} (test) > create table t_cpu(id int primary key auto_increment);Query OK, 0 rows affected (0.39 sec)插入大量模拟测试数据:
mysql [localhost:5727] {msandbox} (test) > insert into t_cpu values(),(),(),(),();Query OK, 5 rows affected (0.06 sec)Records: 5Duplicates: 0Warnings: 0mysql [localhost:5727] {msandbox} (test) > insert into t_cpu select id+(select count(*) from t_cpu) from t_cpu;Query OK, 5 rows affected (0.05 sec)Records: 5Duplicates: 0Warnings: 0............mysql [localhost:5727] {msandbox} (test) > insert into t_cpu select id+(select count(*) from t_cpu) from t_cpu;Query OK, 5242880 rows affected (1 min 8.84 sec)Records: 5242880Duplicates: 0Warnings: 0mysql [localhost:5727] {msandbox} (test) > select count(*) from t_cpu;+----------+| count(*) |+----------+| 10485760 |+----------+1 row in set (2.22 sec)至此我们已经造了 10485760 条数据 。
有 1000W+ 的数据量了,我们模拟一个业务场景,让 CPU 嗨(high)起来 。
执行一个慢查询:
mysql [localhost:5727] {msandbox} (test) > select * from t_cpu order by rand() limit 1;另外开一个会话,top 看看进程:
top - 15:21:20 up 33 days, 23:10,3 users,load average: 0.38, 0.19, 0.21Tasks: 202 total,2 running, 200 sleeping,0 stopped,0 zombie%Cpu(s): 23.3 us,3.6 sy,0.0 ni, 73.1 id,0.0 wa,0.0 hi,0.0 si,0.0 stKiB Mem :8173716 total,2867684 free,1059276 used,4246756 buff/cacheKiB Swap:2097148 total,2097140 free,8 used.6761912 avail MemPID USERPRNIVIRTRESSHR S%CPU %MEMTIME+ COMMAND32232 root200 1443252 35668811748 S 107.04.42:03.82 mysqld296 root20067044155881440 S0.70.2 325:32.46 plymouthd600 root20016203223601580 R0.30.00:04.21 top1901 gnome-i+200714116224369168 S0.30.398:46.20 gsd-color1 root20019126441922632 S0.00.118:39.97 systemd2 root200000 S0.00.00:01.28 kthreadd4 root0 -20000 S0.00.00:00.00 kworker/0:0H6 root200000 S0.00.00:00.95 ksoftirqd/07 rootrt0000 S0.00.00:02.08 migration/08 root200000 S0.00.00:00.00 rcu_bh


推荐阅读