发新话题
打印

mysql Sleep的进程的来源探究[转]

mysql Sleep的进程的来源探究[转]

当前的连接数:
mysql> show status like '%Threads_connected%';
+-------------------+-------+
| Variable_name     | Value |
+-------------------+-------+
| Threads_connected | 27    |
+-------------------+-------+
1 row in set (0.00 sec)

最大连接数:
show variables like '%max_connections%';
set GLOBAL max_connections=800;
flush privileges
也可以修改/etc/my.cnf中的max_connections:
max_connections = 1000

关于php应该在何时调用mysql_close()以及pconnect方式和传统方式有何种区别收藏
以前我一直认为,当php的页面执行结束时,会自动释放掉一切。相信很多人都跟我想的一样。但事实证明并不是这样。比如session就不会随着页面执行完毕而释放。
php的垃圾回收机制,其实只针对于php本身。对于mysql,php没权利去自动去释放它的东西。如果你在页面执行完毕前不调用mysql_close(),那么mysql那边是不会关闭这个连接的。如果你是用的是pconnect方式,即使你在页面执行完毕前调用mysql_close(),也无法另mysql关闭这个连接。
也许在负载低的情况下,你感受不到有何不妥。但是,一旦负载很高,就回出现很多的死链接,于是得杀掉它们,现象:
在php中使用pconnect方式建立连接,然后到mysql客户端下执行show processlist;如果你的负载到一定程度的话,你可以看到很多sleep的进程,这些进程就是人们常说的死连接,它们会一直保持sleep,直到my.cnf里面设置的wait_timeout这个参数值的时间到了,mysql才会自己杀死它。在杀死它的时候,mysql还会在error-log里面记录一条Aborted connection xxx to db: 'xxx' user: 'xxx' host: 'xxx'的日志,用google翻译一下,会得到一个相当强悍的解释"胎死腹中的连接"!
那么造成sleep的原因,有三个,下面是mysql手册给出的解释:
1.客户端程序在退出之前没有调用mysql_close().[写程序的疏忽,或者数据库的db类库没有自动关闭每次的连接。。。]
2.客户端sleep的时间在wait_timeout或interactive_timeout规定的秒内没有发出任何请求到服务器. [类似常连,类似于不完整的tcp ip协议构造,服务端一直认为客户端仍然存在(有可能客户端已经断掉了)]
3.客户端程序在结束之前向服务器发送了请求还没得到返回结果就结束掉了. [参看:tcp ip协议的三次握手]




网上有一个哥们写了一个,如下:

<?php
define('MAX_SLEEP_TIME', 120);

$hostname = "localhost";
$username = "root";
$password = "password";

$connect = mysql_connect($hostname, $username, $password);
$result = mysql_query("SHOW PROCESSLIST", $connect);
while ($proc = mysql_fetch_assoc($result)) {
    if ($proc["Command"] == "Sleep" && $proc["Time"] > MAX_SLEEP_TIME) {
        @mysql_query("KILL " . $proc["Id"], $connect);
    }
}
mysql_close($connect);
?>


将它当中的 $password 改成你实际的数据库密码,死连接的时间也可以修改。然后加入计划任务就可以了。比如用 crontab -e 命令加入:

*/2 * * * * php /usr/local/sbin/kill-mysql-sleep-proc.php就可以每隔 2 分钟检查并清除一次数据库中的死连接了。


我结合自己的实际改写如下:

<?php

require_once 'services/UserServices*.class.php';
define('MAX_SLEEP_TIME', 120);//注意调试的时候这儿只能修改120,而不能在重新定义,常量一旦定义好,就不能被重新定义了。PHP预先定义了几个常量,并提供了一种机制在运行时自己定义。常量和变量基本上是一样的,不同的是:常量必须用DEFINE函数定义,常量一旦定义好,就不能被重新定义了。
$scoreServ = new TMService ( );
$sql = "SHOW PROCESSLIST";
$proc = $scoreServ*->query($sql);
foreach($proc as $oneproc)
{
    if ($oneproc["Command"] == "Sleep" && $oneproc["Time"] >= MAX_SLEEP_TIME)
    {
       $query = "KILL " . $oneproc["Id"];
       echo $query."\n";
       @$scoreServ*->query($query);
    }
}

?>

crontab 加入:

*/2 * * * * /usr/local/php/bin/php /usr/local/tads/htdocs/*/src/crontable/killmysqlsleepproc.php


听说可以做mysql的设置也可以的如下:


配置MYSQL里的参数。超时时间设置。
max_connections:
允许的同时客户的数量。负载过大时,你将经常看到 too many connections 错误。已达到最大链接数,所以会出现这种情况。 我们服务器数值是200。
wait_timeout            
服务器在关闭连接之前在一个连接上等待行动的秒数,默认数值是28800,即如果没有事情发生,服务器在 8个小时后关闭连接。防止sleep过而导致出现too many connections


如果你的sleep进程数在同一时间内过多,再加上其他状态的连接,总数超过了max_connection的值,那mysql除了root用户外,就无法再继续处理任何请求无法与任何请求建立连接或者直接down了。所以,这个问题在大负载的情况下还是相当严重的。如果发现你的mysql有很多死连接存在,首先要先检查你的程序是否使用的是pconnect的方式,其次,检查在页面执行完毕前是否及时调用了mysql_close(),


还有一个办法,你可以在my.cnf里面加上wait_timeout和interactive_timeout,把他们的值设的小一些,默认情况下wait_timeout的值是8小时的时间,你可以改成1个小时,或半个小时。这样mysql会更快的杀死死连接。防止连接总数超过max_connection的值。或者把max_connection的值设置的更大,不过这样显然不妥,连接的数量越多,对你服务器的压力越大。实际上那些连接都是冗余的,把它们尽快杀死才是上策。




以前总是说,在使用php连接mysql的时候,尽量不要使用pconnect的方式,看完我上面所说的那些,应该可以明白为什么了吧,因为我们使用php大多数情况下都是做web开发,web开发是面向多用户,那么用户的数量与mysql连接数是成正比的。使用pconnect的方式,即使你的调用mysql_close()也是无法释放数据库连接的,那么mysql中的死连接的数量就会越来越多了。



我认为,只有当你的应用属于那种点对点方式,或者你能保证连接数量很少的情况,才有必要去采用pconnect的方式,因为连接数量少,那么让它一直处于连接状态,避免了重复打开关闭的过程。这样可能会比传统方式更好一些。



至于何时该去调用mysql_close(),最正确的做法是如果下面不再执行mysql的操作了,在你上一次执行完mysql操作后,立刻就调用mysql_close()。这才是最正确的做法,并不是总要把mysql_close()写在页面最后一行就可以了。

如果你没有修改过MySQL的配置,缺省情况下,wait_timeout的初始值是28800。

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

[mysqld]

wait_timeout=10

# /etc/init.d/mysql restart
复制代码不过这个方法太生硬了,线上服务重启无论如何都应该尽可能避免,看看如何在MySQL命令行里通过SET来设置: mysql> set global wait_timeout=10;

mysql> show global variables like '%timeout';

+----------------------------+-------+

| Variable_name              | Value |

+----------------------------+-------+

| wait_timeout               | 10    |

+----------------------------+-------+
复制代码这里一个容易把人搞蒙的地方是如果查询时使用的是show variables的话,会发现设置好像并没有生效,这是因为单纯使用show variables的话就等同于使用的是show session variables,查询的是会话变量,只有使用show global variables,查询的才是全局变量。

网络上很多人都抱怨说他们set global之后使用show variables查询没有发现改变,原因就在于混淆了会话变量和全局变量,如果仅仅想修改会话变量的话,可以使用类似set wait_timeout=10;或者set session wait_timeout=10;这样的语法。

另一个值得注意的是会话变量wait_timeout初始化的问题,这一点在手册里已经明确指出了,我就直接拷贝了:
On thread startup, the session wait_timeout value is initialized from the global wait_timeout value or from the global interactive_timeout value, depending on the type of client (as defined by the CLIENT_INTERACTIVE connect option to mysql_real_connect()).

MySQL大拿Jeremy Zawodny曾在他的文章Fixing Poor MySQL Default Configuration Values里面列出了几个很恶心的MySQL缺省设置,不过没包含wait_timeout,但我觉得它也应该算一个,每次新装MySQL后最好都记得修改它。

以上的修改配置来源网页:http://www.tech-q.cn/redirect.php?tid=4005&goto=lastpost


mysql>show variables like '%timeout';
打印结果如下:
+----------------------------+-------+
| Variable_name | Value |
+----------------------------+-------+
| connect_timeout | 5 |
| delayed_insert_timeout | 300 |
| interactive_timeout | 28800 |
| net_read_timeout | 30 |
| net_write_timeout | 60 |
| slave_net_timeout | 3600 |
| wait_timeout | 28800 |
+----------------------------+-------+

interactive_timeout 需在mysql_connect()设置CLIENT_INTERACTIVE选项后起作用,并被赋值为wait_timeout;
mysql>set wait_timeout = 10; 对当前交互链接有效;
mysql>set interactive_timeout = 10; 对后续起的交互链接有效;

该超时时间单位是秒,从变量从上次SQL执行后算起;当前空闲若超过该时间,则也会被强制断开。


想把mysql的连接断开时间改长一些,以前只改了connect_timeout变量的值,还不够。现在又改了这两个,不知够不够。不够再继续查吧。

注意:对两个值都做修改才生效:set interactive_timeout=120; set wait_timeout=120;


mysql> show variables like '%timeout';
+-------------------------+-------+
| Variable_name           | Value |
+-------------------------+-------+
| connect_timeout         | 5     |
| delayed_insert_timeout  | 300   |
| interactive_timeout     | 28800 |
| net_read_timeout        | 30    |
| net_write_timeout       | 60    |
| slave_net_timeout       | 3600  |
| table_lock_wait_timeout | 50    |
| wait_timeout            | 28800 |
+-------------------------+-------+


mysql> set interactive_timeout=120; set wait_timeout=120;
Query OK, 0 rows affected (0.00 sec)

mysql> show variables like '%timeout';
+-------------------------+-------+
| Variable_name           | Value |
+-------------------------+-------+
| connect_timeout         | 5     |
| delayed_insert_timeout  | 300   |
| interactive_timeout     | 120   |
| net_read_timeout        | 30    |
| net_write_timeout       | 60    |
| slave_net_timeout       | 3600  |
| table_lock_wait_timeout | 50    |
| wait_timeout            | 120   |
+-------------------------+-------+

修改全局变量:
set global interactive_timeout=120;set global wait_timeout=120;


mysql> show global variables like '%timeout';
+-------------------------+-------+
| Variable_name           | Value |
+-------------------------+-------+
| connect_timeout         | 5     |
| delayed_insert_timeout  | 300   |
| interactive_timeout     | 120   |
| net_read_timeout        | 30    |
| net_write_timeout       | 60    |
| slave_net_timeout       | 3600  |
| table_lock_wait_timeout | 50    |
| wait_timeout            | 120   |
+-------------------------+-------+

特别注意全局和一般变量时不一样的两个变量,这也就是为何导致修改没有起作用的原因!!!!



配置修改:
直接的修改 /etc/my.cnf这个文件中

-------------------------------------------
[mysqld]

wait_timeout = 86400
interactive_timeout = 86400
--------------------------------------------
添加这两行,然后重新启动mysql服务就OK了
文章来源:http://blog.chinaunix.net/u2/60332/showart_2096857.html

近一段时间,部门同事反映在使用mysql的过程出现数据库连接问题

应用程序和数据库建立连接,如果超过8小时应用程序不去访问数据库,数据库就断掉连接 。这时再次访问就会抛出异常,如下所示:

java.io.EOFException
    at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:1913)
    at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:2304)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:2803)
    at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1573)
...

查了一下发现应用程序和mysql数据库建立连接,如果超过8小时应用程序不去访问数据库,数据库就断掉连接 。这时再次访问就会抛出异常。

关于mysql自动断开的问题研究结果如下,在mysql中有相关参数设定,当数据库连接空闲一定时间后,服务器就会断开等待超时的连接:
1、相关参数,红色部分
mysql> show variables like '%timeout%';
+--------------------------+-------+
| Variable_name            | Value |
+--------------------------+-------+
| connect_timeout          | 5     |
| delayed_insert_timeout   | 300   |
| innodb_lock_wait_timeout | 50    |
| interactive_timeout      | 28800 |
| net_read_timeout         | 30    |
| net_write_timeout        | 60    |
| slave_net_timeout        | 3600 |
| wait_timeout             | 28800 |
+--------------------------+-------+         
同一时间,这两个参数只有一个起作用。到底是哪个参数起作用,和用户连接时指定的连接参数相关,缺省情况下是使用wait_timeout。我建议是将这两个参数都修改,以免引起不必要的麻烦。


2、修改参数
这两个参数的默认值是8小时(60*60*8=28800)。我测试过将这两个参数改为0,结果出人意料,系统自动将这个值设置为1。换句话说,不能将该值设置为永久。
将这2个参数设置为24小时(60*60*24=604800)即可。
set interactive_timeout=604800;
set wait_timeout=604800;

也可以修改my.cof,修改后重起mysql
打开/etc/my.cnf,在属性组mysqld下面添加参数如下:
[mysqld]
interactive_timeout=28800000
wait_timeout=28800000

如果一段时间内没有数据库访问则mysql自身将切断连接,之后访问java访问连接池时对数据库的数据通道早就关闭了,因为dbcp连接池无法时时维护与数据库的连接关系,mysql5以后即使在dbcp配置中加入autoReconnect=true也没有效果。


言归正传,接着来,shell脚本实现如下:

#!/bin/sh
注:这个脚本运行后会每五秒去检测一次 mysql的sleep进程数

while :
do
  n=`/usr/bin/mysqladmin processlist | grep -i sleep | wc -l`
  date=`date +%Y%m%d\[%H:%M:%S]`
  echo $n

  if [ "$n" -gt 10 ]
  then
    for i in `/usr/bin/mysqladmin processlist | grep -i sleep | awk '{print $2}'`
    do
      /usr/bin/mysqladmin kill $i
    done
    echo "sleep is too many i killed it" >> /tmp/sleep.log
    echo "$date : $n" >> /tmp/sleep.log
  fi
sleep 5
done


crontab实现:不会循环的脚本(配合crontab使用)

#!/bin/sh
n=`/usr/bin/mysqladmin processlist | grep -i sleep | wc -l`
date=`date +%Y%m%d\[%H:%M:%S]`
echo $n
if [ "$n" -gt 10 ]
then
for i in `/usr/bin/mysqladmin processlist | grep -i sleep | awk '{print $2}'`
do
/usr/bin/mysqladmin kill $i
done
echo "sleep is too many i killed it" >> /tmp/sleep.log
echo "$date : $n" >> /tmp/sleep.log
fi


你可能还会用到查询mysql当前连接数:
1.show status
   Threads_connected  当前的连接数
   Connections  试图连接到(不管是否成功)MySQL服务器的连接数。
   Max_used_connections  服务器启动后已经同时使用的连接的最大数量。
2.set GLOBAL max_connections=连接数;
   flush privileges
3.修改/etc/my.cnf中的max_connections
4.show processlist   显示当前正在执行的mysql连接
5.mysqladmin -u<user> -p<pwd> -h<host> status
   显示当前mysql状态
   Uptime: 13131  Threads: 1  Questions: 22  Slow queries: 0  Opens: 16  Flush tables: 1  Open tables: 1  Queries per second avg: 0.1
   mysqladmin -u<user> -p<pwd> -h<host> extended-status
   显示mysql的其他状态
+-----------------------------------+----------+
| Variable_name                     | Value    |
+-----------------------------------+----------+
| Aborted_clients                   | 0        |
| Aborted_connects               | 1        |
| Binlog_cache_disk_use       | 0        |
| Binlog_cache_use               | 0        |
| Bytes_received                   | 1152   |
| Bytes_sent                         | 10400 |
| Com ......

TOP

发新话题