首先感谢以下两边文章的作者,我是参阅了2位作者的文章之后,经过测试加入自己的理解。
参考:
说说mysql_connect和mysql_pconnect的区别,这俩函数用法上差不多,网上有说应该用pconnect的,pconnect是个好东西;也有视pconnect如洪水猛兽的,坚决不让用pconnect的,也有态度暧昧不清的。那这个东西到底如何呢?
永久链接并不是说,服务器打开了一个连接,然后所有的人都共享这个链接。永久连接一样是每个客户端来就打开一个连接,有200人访问就有200个连接。其实mysql_pconnect()本身并没有做太多的处理, 它唯一做的只是在php运行结束后不主动close掉mysql的连接.
在php经cgi方式运行时pconnect和connect是基本没有区别的, 因为cgi方式是每一个php访问起一个进程, 访问结束后进程也就结束了, 资源也全释放了.当php以apache模块方式运行时(deven,现在大多数服务器都是以这种方式运行的),由于apache有使用进程池, 一个httpd进程结束后会被放回进程池,这也就使得用pconnect打开的的那个mysql连接资源不被释放, 于是有下一个连接请求时就可以被复用.这就使得在apache并发访问量不大的时候,由于使用了pconnect, php节省了反复连接db的时间, 使得访问速度加快. 这应该是比较好理解的. 但是在apache并发访问量大的时候, 如果使用pconnect, 会由于之前的一些httpd进程占用的mysql连接没有close,则可能会因为mysql已经达到最大连接着, 使得之后的一些请求永远得不到满足.若mysql最大连接数设为500, 而apache的最大同时访问数设为2000,假设所有访问都会要求访问db, 而且操作时间会比较长,当前500个请求的httpd都没有结束的时候,之后的httd进程都是无法连接到mysql的(因已经达到mysql最大连接数). 只有当前500个httpd进程结束或被复用才可以连接得到了mysql.
当db操作复杂, 耗时较长时, 因httpd会fork很多并发进程处理, 而先产生的httpd进程不释放db连接, 使得后产生的httpd进程无法连上db. 因为这样没有复用其它httpd进程的mysql连接. 于是会就产生很多连接超时。 在并发访问量不高时,使用pconnect可以简单提高访问速度, 但在并发量增大后, 是否再使用pconnect就要看程序员的选择了.
就我个人认为, php现在对mysql的连接并没有真正用到连接池, pconnect也只是相当于借了apache的进程池来用, 所以在并发访问量大的时候pconnect并不能很好的提高访问db效率.
在实际的应用中,用mysql_pconnect的话,每次刷新和请求新的页面都比较快,而用mysql_connect的话,每次刷新都要重新请求,当数据库连接比较慢的时候,就能看出差异了。当你的数据库连接比较慢,DB操作不是很复杂,并且你的程序足够自信,不会产生死锁的时候,或者你拥有对服务器的控制权,满足以上四个条件中的任意两个,那就可以用pconnect。
(1)以下是我通过浏览器(by deven)的测试结果:
1.当不断的刷新页面,使用pconnect连接的时候,不会主动关闭mysql连接,只有当sleep时间超过wait_timeout值时才会关闭。
2.如果使用短连接connect,不断刷新之后去看SHOW PROCESSLIST 状态,看不到任何sleep进程,证明会快速自动释放连接,但是设置wait_timeout对短连接connect还是起作用的。
(2)以下是我通过ssh登录服务器测试的结果。
首先执行sql语句:select * from user where user='root';
过了10s之后再次执行select * from user where user='root';就会提示连接数据库失败,其实它是重新建立了一个连接。
select * from user where user='root';
ERROR 2006 (HY000): MySQL server has gone away
No connection. Trying to reconnect...
Connection id: 49
Current database: mysql
mysql_connect() 函数打开非持久的 MySQL 连接。
语法
mysql_connect(server,user,pwd,newlink,clientflag)
参数 | 描述 |
---|---|
server | 可选。规定要连接的服务器。 可以包括端口号,例如 "hostname:port",或者到本地套接字的路径,例如对于 localhost 的 ":/path/to/socket"。 如果 PHP 指令 mysql.default_host 未定义(默认情况),则默认值是 'localhost:3306'。 |
user | 可选。用户名。默认值是服务器进程所有者的用户名。 |
pwd | 可选。密码。默认值是空密码。 |
newlink | 可选。如果用同样的参数第二次调用 mysql_connect(),将不会建立新连接,而将返回已经打开的连接标识。参数 new_link 改变此行为并使 mysql_connect() 总是打开新的连接,甚至当 mysql_connect() 曾在前面被用同样的参数调用过。 |
clientflag | 可选。client_flags 参数可以是以下常量的组合:
|
返回值
如果成功,则返回一个 MySQL 连接标识,失败则返回 FALSE。
以下是mysql_pconnect()函数用法:
mysql_pconnect() 函数打开一个到 MySQL 服务器的持久连接。
mysql_pconnect() 和 mysql_connect() 非常相似,但有两个主要区别:
- 当连接的时候本函数将先尝试寻找一个在同一个主机上用同样的用户名和密码已经打开的(持久)连接,如果找到,则返回此连接标识而不打开新连接。
- 其次,当脚本执行完毕后到 SQL 服务器的连接不会被关闭,此连接将保持打开以备以后使用(mysql_close() 不会关闭由 mysql_pconnect() 建立的连接)。
语法
mysql_pconnect(server,user,pwd,clientflag)
参数 | 描述 |
---|---|
server | 可选。规定要连接的服务器。 可以包括端口号,例如 "hostname:port",或者到本地套接字的路径,例如对于 localhost 的 ":/path/to/socket"。 如果 PHP 指令 mysql.default_host 未定义(默认情况),则默认值是 'localhost:3306'。 |
user | 可选。用户名。默认值是服务器进程所有者的用户名。 |
pwd | 可选。密码。默认值是空密码。 |
clientflag | 可选。client_flags 参数可以是以下常量的组合:
|
返回值
如果成功,则返回一个 MySQL 持久连接标识符,出错则返回 FALSE。
关于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'的日志(deven,我看了日志,好像没有),用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秒的话,那么这个设置就有问题了(deven,通过终端登录测试就可以看到效果了)(当然,这也不是不能解决的问题,你可以在程序里时不时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后最好都记得修改它。
interactive_timeout 需在mysql_connect()设置CLIENT_INTERACTIVE选项后起作用(我看了以下我们生成环境,都没有设置CLIENT_INTERACTIVE选项,by deven),并被赋值为wait_timeout; mysql>set wait_timeout = 10; 对当前交互链接有效; mysql>set interactive_timeout = 10; 对后续起的交互链接有效; 该超时时间单位是秒,从变量从上次SQL执行后算起;当前空闲若超过该时间,则也会被强制断开。 特别注意全局和一般变量时不一样的两个变量,这也就是为何导致修改没有起作用的原因!!!! 配置修改: 直接的修改 /etc/my.cnf这个文件中 ------------------------------------------- [mysqld] wait_timeout = 86400 interactive_timeout = 86400 -------------------------------------------- 添加这两行,然后重新启动mysql服务就OK了 应用程序和数据库建立连接,如果超过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小时应用程序不去访问数据库,数据库就断掉连接 。这时再次访问就会抛出异常。 注:wait_timeout与interactive_timeout设多大可试试,小一点可空出进程,较快断开,大了要等较长时间清冗余线程。
之前deven一直以为短连接不会有sleep进程,因为我拼命的刷新页面,还是看到不到sleep进程,然后看了以下文章才醒悟过来。
A,硬盘上存在大量的静态文件,或者WEB服务器负荷太重,在处理HTTP请求响应变得太慢,这样也有可能导致出现大量Sleep进程,解决方法适当调整WEB服务参数和文件,一味的静态或者缓存化网页内容并不是灵丹妙药。
B,在网页脚本中,有些计算和应用可能非常耗时,比如在0秒的时候打开数据库执行完一段SQL代码后,网页脚本随即花了20秒钟进行一段复杂的运 算,或者是require了一个庞大的PHP文件(比如含有几千个违规关键字的过滤函数),哪么这个时候在MySQL后台看到的进程中,这个20秒的过程 MySQL并没有做任何事情了,一直处于Sleep状态,直到这个页面执行完毕或者达到wait_timeout值(被强行关闭),优化网页脚本,尽量让 程序快速运行,或者在执行这段耗时的运行过程中,执行mysql_close把当前MySQL链接强行关闭。
C,在采集站中,MySQL中大量的Sleep进程这类现象尤其明显(比如很多网友问道DeDeCMS的MySQL中出现大量Sleep),因为大 部的采集器页面在运行过程中,事先打开了一个MySQL链接(可能是为了验证用户权限等),然后开始使用file_get_contents之类的操作去 获取一个远程的网页内容,如果这个远程的站点访问速度太慢,比如花了10秒时间才把网页取回,哪么当前采集脚本程序就一直阻塞在这里,并且MySQL啥事 也没干,一直处于Sleep状态。解决方法同上,在发出file_get_contents采集远程网页的时候,使用mysql_close强行关闭 MySQL的连接,等采集完成在适当需要的时候再重新mysql_connect即可。
因此deven马上做了测试,模拟sleep情况:
$num = $db->result($db->query("SELECT COUNT(*) FROM `user`),0);
sleep(15);
$num = $db->result($db->query("SELECT COUNT(*) FROM `user`),0);
echo file_get_contents("http://www.miibeian.gov.cn/");
$num = $db->result($db->query("SELECT COUNT(*) FROM `user`),0);
解决MySQL server has gone away
1、应用程序(比如PHP)长时间的执行批量的MYSQL语句。最常见的就是采集或者新旧数据转化。
解决方案:
在my.cnf文件中添加或者修改以下两个变量:
wait_timeout=300
interactive_timeout = 300
关于两个变量的具体说明可以google或者看官方手册。如果不能修改my.cnf,则可以在连接数据库的时候设置CLIENT_INTERACTIVE,比如:sql = "set interactive_timeout=24*3600";
mysql_real_query(...)