MySQL CPU占用过高怎么办

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL CPU占用过高怎么办相关的知识,希望对你有一定的参考价值。

通过以前对mysql的操作经验,先将mysql的配置问题排除了,查看msyql是否运行正常,通过查看mysql data目录里面的*.err文件(将扩展名改为.txt)记事本查看即可。如果过大不建议用记事本了,容易死掉,可以用editplus等工具。

简单的分为下面几个步骤来解决这个问题:

1、mysql运行正常,也有可能是同步设置问题导致

2、如果mysql运行正常,那就是php的一些sql语句导致问题发现,用root用户进入mysql管理

mysql -u root -p

输入密码

mysql:show processlist 语句,查找负荷最重的 SQL 语句,优化该SQL,比如适当建立某字段的索引。

通过这个命令我看到原来是有人恶意刷搜索,因为dedecms搜索后面调用搜索最高的词,导致很多人用工具刷这个,而且是定时有间隔的,所以将这个php程序改名跳转都方法解决了。

当然如果你的确实是sql语句用了大量的group by等语句,union联合查询等肯定会将mysql的占用率提高。所以就需要优化sql语句,网站尽量生成静态的,一般4W ip的静态网站,mysql占用率几乎为0的。所以这对于程序员的经验是个考虑。尽量提高mysql性能 (MySQL 性能优化的最佳20多条经验分享)

下面是豆芽收集的文章,大家都可以参考下

MYSQL CPU 占用 100% 的现象描述

早上帮朋友一台服务器解决了 Mysql cpu 占用 100% 的问题。稍整理了一下,将经验记录在这篇文章里

朋友主机(Windows 2003 + IIS + PHP + MYSQL )近来 MySQL 服务进程 (mysqld-nt.exe) CPU 占用率总为 100% 高居不下。此主机有10个左右的 database, 分别给十个网站调用。据朋友测试,导致 mysqld-nt.exe cpu 占用奇高的是网站A,一旦在 IIS 中将此网站停止服务,CPU 占用就降下来了。一启用,则马上上升。

MYSQL CPU 占用 100% 的解决过程

今天早上仔细检查了一下。目前此网站的七日平均日 IP 为2000,PageView 为 3万左右。网站A 用的 database 目前有39个表,记录数 60.1万条,占空间 45MB。按这个数据,MySQL 不可能占用这么高的资源。

于是在服务器上运行命令,将 mysql 当前的环境变量输出到文件 output.txt:

d:\web\mysql> mysqld.exe --help >output.txt

发现 tmp_table_size 的值是默认的 32M,于是修改 My.ini, 将 tmp_table_size 赋值到 200M:

d:\web\mysql> notepad c:\windows\my.ini

[mysqld]

tmp_table_size=200M

然后重启 MySQL 服务。CPU 占用有轻微下降,以前的CPU 占用波形图是 100% 一根直线,现在则在 97%~100%之间起伏。这表明调整 tmp_table_size 参数对 MYSQL 性能提升有改善作用。但问题还没有完全解决。

于是进入 mysql 的 shell 命令行,调用 show processlist, 查看当前 mysql 使用频繁的 sql 语句:

mysql> show processlist;

反复调用此命令,发现网站 A 的两个 SQL 语句经常在 process list 中出现,其语法如下:

SELECT t1.pid, t2.userid, t3.count, t1.date

FROM _mydata AS t1

LEFT JOIN _myuser AS t3 ON t1.userid=t3.userid

LEFT JOIN _mydata_body AS t2 ON t1.pid=t3.pid

ORDER BY t1.pid

LIMIT 0,15

调用 show columns 检查这三个表的结构 :

mysql> show columns from _myuser;

mysql> show columns from _mydata;

mysql> show columns from _mydata_body;

终于发现了问题所在:_mydata 表,只根据 pid 建立了一个 primary key,但并没有为 userid 建立索引。而在这个 SQL 语句的第一个 LEFT JOIN ON 子句中:

LEFT JOIN _myuser AS t3 ON t1.userid=t3.userid

_mydata 的 userid 被参与了条件比较运算。于是我为给 _mydata 表根据字段 userid 建立了一个索引:

mysql> ALTER TABLE `_mydata` ADD INDEX ( `userid` )

建立此索引之后,CPU 马上降到了 80% 左右。看到找到了问题所在,于是检查另一个反复出现在 show processlist 中的 sql 语句:

SELECT COUNT(*)

FROM _mydata AS t1, _mydata_key AS t2

WHERE t1.pid=t2.pid and t2.keywords = '孔雀'

经检查 _mydata_key 表的结构,发现它只为 pid 建了了 primary key, 没有为 keywords 建立 index。_mydata_key 目前有 33 万条记录,在没有索引的情况下对33万条记录进行文本检索匹配,不耗费大量的 cpu 时间才怪。看来就是针对这个表的检索出问题了。于是同样为 _mydata_key 表根据字段 keywords 加上索引:

mysql> ALTER TABLE `_mydata_key` ADD INDEX ( `keywords` )

建立此索引之后,CPU立刻降了下来,在 50%~70%之间震荡。

再次调用 show prosslist,网站A 的sql 调用就很少出现在结果列表中了。但发现此主机运行了几个 Discuz 的论坛程序, Discuz 论坛的好几个表也存在着这个问题。于是顺手一并解决,cpu占用再次降下来了。(2007.07.09 附注:关于 discuz 论坛的具体优化过程,我后来另写了一篇文章,详见:千万级记录的 Discuz! 论坛导致 MySQL CPU 100% 的 优化笔记 )

解决 MYSQL CPU 占用 100% 的经验总结

增加 tmp_table_size 值。mysql 的配置文件中,tmp_table_size 的默认大小是 32M。如果一张临时表超出该大小,MySQL产生一个 The table tbl_name is full 形式的错误,如果你做很多高级 GROUP BY 查询,增加 tmp_table_size 值。

对 WHERE, JOIN, MAX(), MIN(), ORDER BY 等子句中的条件判断中用到的字段,应该根据其建立索引 INDEX。索引被用来快速找出在一个列上用一特定值的行。没有索引,MySQL不得不首先以第一条记录开始并然后读完整个表直到它找出相关的行。表越大,花费时间越多。如果表对于查询的列有一个索引,MySQL能快速到达一个位置去搜寻到数据文件的中间,没有必要考虑所有数据。如果一个表有1000行,这比顺序读取至少快100倍。所有的MySQL索引(PRIMARY、UNIQUE和INDEX)在B树中存储。

根据 mysql 的开发文档:

索引 index 用于:

快速找出匹配一个WHERE子句的行

当执行联结(JOIN)时,从其他表检索行。

对特定的索引列找出MAX()或MIN()值

如果排序或分组在一个可用键的最左面前缀上进行(例如,ORDER BY key_part_1,key_part_2),排序或分组一个表。如果所有键值部分跟随DESC,键以倒序被读取。

在一些情况中,一个查询能被优化来检索值,不用咨询数据文件。如果对某些表的所有使用的列是数字型的并且构成某些键的最左面前缀,为了更快,值可以从索引树被检索出来。

假定你发出下列SELECT语句:

mysql> SELECT * FROM tbl_name WHERE col1=val1 AND col2=val2;

如果一个多列索引存在于col1和col2上,适当的行可以直接被取出。如果分开的单行列索引存在于col1和col2上,优化器试图通过决定哪个索引将找到更少的行并来找出更具限制性的索引并且使用该索引取行。
参考技术A 先 找到 CPU 高的线程,如果 CPU 高的线程号一直在变,那可能不是单个 SQL 引起的 CPU 消耗,需要用其他方法来辅助分析。找到线程任务processlist 。
可以看到很多有用的信息:
1. 可以看到 processlist 中对应这根线程的信息
2. 可以找到其在 processlist 中的 ID,这样我们就可以下 kill 命令来结束 SQL
小贴士:
使用 performance_schema 时,需要大家注意 MySQL 使用了多个线程编号,源自于不同视角:
1. PROCESSLIST_ID:在 processlist 中的编号,是使用者视角的编号,使用者可以直接用 kill 命令。
2. THREAD_ID:是 MySQL 内部使用的线程编号,是 MySQL 内部视角的编号。
3. THREAD_OS_ID:是在操作系统上,对应的线程编号,是操作系统视角的编号。
大家使用时需要区分好,不要 kill 错了 SQL。
其他有用的信息,可以看到 SQL 执行的开始时间,正在使用了一张临时磁盘表。
如果开启了 performance_schema 的其他监控项,通过 Thread_ID 关联,可以找到更多信息。
当然,眼下这么明显的坑 SQL,我们 kill 掉就是了。

Mysql占用过高CPU时的优化手段

Mysql占用CPU过高的时候,该从哪些方面下手进行优化?
占用CPU过高,可以做如下考虑:
1)一般来讲,排除高并发的因素,还是要找到导致你CPU过高的哪几条在执行的SQL,show processlist语句,查找负荷最重的SQL语句,优化该SQL,比如适当建立某字段的索引;
2)打开慢查询日志,将那些执行时间过长且占用资源过多的SQL拿来进行explain分析,导致CPU过高,多数是GroupBy、OrderBy排序问题所导致,然后慢慢进行优化改进。比如优化insert语句、优化group by语句、优化order by语句、优化join语句等等;
3)考虑定时优化文件及索引;
4)定期分析表,使用optimize table;
5)优化数据库对象;
6)考虑是否是锁问题;
7)调整一些MySQL Server参数,比如key_buffer_size、table_cache、innodb_buffer_pool_size、innodb_log_file_size等等;
8)如果数据量过大,可以考虑使用MySQL集群或者搭建高可用环境。
9)可能由于内存latch(泄露)导致数据库CPU高
10)在多用户高并发的情况下,任何系统都会hold不住的,所以,使用缓存是必须的,使用memcached或者redis缓存都可以;
11)看看tmp_table_size大小是否偏小,如果允许,适当的增大一点;
12)如果max_heap_table_size配置的过小,增大一点;
13)mysql的sql语句睡眠连接超时时间设置问题(wait_timeout)
14)使用show processlist查看mysql连接数,看看是否超过了mysql设置的连接数(http://www.cnblogs.com/kevingrace/p/6226324.html

MySQL负载居高不下,如果打开了慢查询日志功能,最好的办法就是针对慢查询日志里执行慢的sql语句进行优化,如果sql语句用了大量的group by等语句,union联合查询等肯定会将mysql的占用率提高。所以就需要优化sql语句

除了优化sql语句外,也可以做一些配置上的优化。在mysql中运行show proceslist;出现下面回显结果:
1.查询有大量的Copying to tmp table on disk状态
明显是由于临时表过大导致mysql将临时表写入硬盘影响了整体性能。

Mysql中tmp_table_size的默认值仅为16MB,在当前的情况下显然是不够用的。
mysql> show variables like "%tmp%";
+-------------------+----------+
| Variable_name | Value |
+-------------------+----------+
| max_tmp_tables | 32 |
| slave_load_tmpdir | /tmp |
| tmp_table_size | 16777216 |
| tmpdir | /tmp |
+-------------------+----------+
4 rows in set (0.00 sec)

解决办法:调整临时表大小
1)进mysql终端命令修改,加上global,下次进mysql就会生效
mysql> set global tmp_table_size=33554432;
Query OK, 0 rows affected (0.00 sec)

再次登陆mysql
mysql> show variables like "%tmp%";
+-------------------+----------+
| Variable_name | Value |
+-------------------+----------+
| max_tmp_tables | 32 |
| slave_load_tmpdir | /tmp |
| tmp_table_size | 33554432 |
| tmpdir | /tmp |
+-------------------+----------+
4 rows in set (0.01 sec)

2)my.cnf配置文件修改
[root@www ~]# vim my.cnf
.....
tmp_table_size = 32M

重启mysql
[root@www ~]# /etc/init.d/mysqld restart

2.show processlist;命令的输出结果显示了有哪些线程在运行,可以帮助识别出有问题的查询语句。比如下面结果:
Id User Host db Command Time State Info 
207 root 192.168.1.25:51718 mytest Sleep 5 NULL 
先简单说一下各列的含义和用途,第一列,id,不用说了吧,一个标识,你要kill一个语句的时候很有用。user列,显示单前用户,如果不是root,这个命令就只显示你权限范围内的sql语句。host列,显示这个语句是从哪个ip的哪个端口上发出的。呵呵,可以用来追踪出问题语句的用户。db列,显示这个进程目前连接的是哪个数据库 。command列,显示当前连接的执行的命令,一般就是休眠(sleep),查询(query),连接(connect)。time列,此这个状态持续的时间,单位是秒。state列,显示使用当前连接的sql语句的状态,很重要的列,后续会有所有的状态的描述,请注意,state只是语句执行中的某一个状态,一个sql语句,已查询为例,可能需要经过copying to tmp table,Sorting result,Sending data等状态才可以完成,info列,显示这个sql语句,因为长度有限,所以长的sql语句就显示不全,但是一个判断问题语句的重要依据。
常见问题:
一般是睡眠连接过多,严重消耗mysql服务器资源(主要是cpu, 内存),并可能导致mysql崩溃。

解决办法 :
在mysql的配置my.cnf文件中,有一项wait_timeout参数设置.即可设置睡眠连接超时秒数,如果某个连接超时,会被mysql自然终止。 
wait_timeout过大有弊端,其体现就是MySQL里大量的SLEEP进程无法及时释放,拖累系统性能,不过也不能把这个指设置的过小,否则你可能会遭遇到“MySQL has gone away”之类的问题。
通常来说,把wait_timeout设置为10小时是个不错的选择,但某些情况下可能也会出问题,比如说有一个CRON脚本,其中两次SQL查询的间隔时间大于10秒的话,那么这个设置就有问题了(当然,这也不是不能解决的问题,你可以在程序里时不时mysql_ping一下,以便服务器知道你还活着,重新计算wait_timeout时间):

MySQL服务器默认的“wait_timeout”是28800秒即8小时,意味着如果一个连接的空闲时间超过8个小时,MySQL将自动断开该连接。
然而连接池却认为该连接还是有效的(因为并未校验连接的有效性),当应用申请使用该连接时,就会导致下面的报错:
The last packet successfully received from the server was 596,688 milliseconds ago.
mysql> show variables like \'wait_timeout\';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| wait_timeout | 28800 |
+---------------+-------+
1 row in set (0.00 sec)

28800seconds,也就是8小时。
如果在wait_timeout秒期间内,数据库连接(java.sql.Connection)一直处于等待状态,mysql就将该连接关闭。这时,你的Java应用的连接池仍然合法地持有该连接的引用。当用该连接来进行数据库操作时,就碰到上述错误。
可以将mysql全局变量wait_timeout的缺省值改大。
查看mysql手册,发现对wait_timeout的最大值分别是24天/365天(windows/linux)。

比如将其改成30天
mysql> set global wait_timeout=124800;
Query OK, 0 rows affected (0.00 sec)

 

以上是关于MySQL CPU占用过高怎么办的主要内容,如果未能解决你的问题,请参考以下文章

MySQL CPU占用过高怎么办

mysqld占用CPU过高是啥原因

mysql经常占用cpu100%以上,该怎么解决

mssql数据库占用CPU过高?

电脑cpu占用过高怎么办解决方法

linux java cpu占用过高怎么办