perl dbi mysql server has gone away 解决办法
2014-06-20 11:31:16 阿炯

本站赞助商链接,请多关照。 在应用脚本建立好连接后,如果久不通信,之前建立的连接超时并被系统注销,这种注销行为并不会通知到应用程序,应用以为这个连接会话还存在,所以会报出这种错误。可以在执行SQL前做个判断,发现Connection不可用时重新连接一下。

就是因为IDLE时间太长了。是可以在my.cnf里设置的:
wait_timeout=28800
interactive_timeout = 28800

即默认8个小时后,没有通信的话这个连接就无效了。如果修改这个会话级的参数能解决上述问题的话,可以适当地修改,但不推荐。当然也可以不定时地发送无关的请求(心跳信号)来维持这个会话。
sql = "set interactive_timeout=24*3600";


另外的一种情况,就是所执行的sql语句太长了,以致超过了max_allowed_packet的大小,也会引起这种情况。可以在'my.cnf'中修改。

还有的情况就是会话被人为的kill了。

当然也可能是程序自己的问题,在perl中使用多进程编程,在主进程中建立了连接句柄,子进程中如果使用这些已经建立的句柄后就会报出这种错误,进程与线程不一样,不能共享父进程的环境变量。

可以的解决办法:
在连接句柄中设定:mysql_auto_reconnect,这样在发现会话连接失效后自动重连。

在perl中的解决办法:
在DBD::mysql 4.012以后支持DBI连接的自动重连
$dbh->{'AutoCommit'} = 1;
$dbh->{'mysql_auto_reconnect'} = 1;

在之前的版本中,不支持自动重连,需要做如下的设置:
$dbh->do('set SESSION wait_timeout=24*3600');
$dbh->do('set SESSION interactive_timeout=72000');

比较完整的写法:
my ($dbhost,$dbuser,$dbpaswd,$db)=('127.0.0.1','dba','password','freeoa');
my %mydbattr=(
 RaiseError=>1,
 AutoCommit=>1,
 mysql_enable_utf8=>1,
 RaiseError=>1,
 mysql_auto_reconnect=>1
);

#create database handle
my $dbh=DBI->connect_cached("DBI:mysql:database=$db;host=$dbhost",$dbuser,$dbpaswd) or die $DBI::errstr;

注意,在设置数据库的属性时,需要开启'AutoCommit',不然自动重新连接不能实现。dbd::mysql的官方文档中也有说明:

This attribute determines whether DBD::mysql will automatically reconnect to mysql if the connection be lost. This feature defaults to off; however, if either the GATEWAY_INTERFACE or MOD_PERL environment variable is set, DBD::mysql will turn mysql_auto_reconnect on. Setting mysql_auto_reconnect to on is not advised if 'lock tables' is used because if DBD::mysql reconnect to mysql all table locks will be lost. This attribute is ignored when AutoCommit is turned off, and when AutoCommit is turned off, DBD::mysql will not automatically reconnect to the server.

而在cgi模式或mod_perl这样的web应用中,这种特性是自动开启的。


参考链接
mysql server has gone away的原因

Perl DBI操作MySQL的Tips