场景:采用Solr/Lucene进行站内文章系统的全文检索实现。
具体环节:索引重建过程,遍历数据库将所有文章条目加入Redis队列服务器,通过无限循环监测Redis队列新弹出值,将对应弹出值的数据库数据创建为solr/lucene文档,进行索引存储。
问题来源:redis队列采用while(true)进行无限循环监测新弹出值的,如果$Redis->rPop(‘队列名’)的新弹出值为空,则while循环继续监测(等待),如果一直没有新的弹出值,并且这个等待时间超过了mysql设定的wait_timeout全局变量值,那么当新的弹出值到来的时候,mysql连接却丢失了,采用mysql单例模型的连接方法,会在mysql_query执行sql的时候报出”Mysql server has gone away”的错误。
问题解决办法:
每次执行mysql_query之前,采用mysql_ping()命令”touch”一下mysql,如果mysql连接已断开,则重新创建一个连接供mysql_query使用。
代码贴出来:
class Article_UpdateController extends Zend_Controller_Action
{
/**
* mysql数据库操作对象
* @var $_mysql_handler object
*/
private$_mysql_handler= null;
private$_mysql_config=array(
‘host’=>‘127.0.0.1’,
‘username’=>‘root’,
‘password’=>‘pwd’,
‘dbname’=>‘dbname’,
‘port’=>‘3306’,
‘charset’=>‘utf8’
);
/**
* 商品搜索索引更新
*/
public functionindexAction()
{
/*无关程序省略*/
$redis= new Redis();
$redis->pconnect(REDIS_HOST,REDIS_PORT);
/**
* 出队数量记录
*/
$record= 0;
/**
* 循环开始时间
*/
$start_time=gmtime();
/**
* 初始化solr搜索
*/
/*无关程序省略*/
/*****************************
* 建立Mysql长连接
* @see
* 1.无限循环的Redis队列需要持续对Mysql的访问场景
* 2.系统Mysql的连接等待时间为10秒
* 3.单例Mysql对象超出10秒重新发起的查询请求被拒绝,并报告”mySQL server has gone away”的错误信息
* 解决方法:
* 查询执行之前先进行一次mysql_ping()操作,如果当前连接已断开,则重新建立连接。
* 其他说明:
* 事实上,Mysql自身具有连接断开自动重连机制,使用mysql_options($link,MYSQL_OPT_RECONNECT,1)函数设定即可,
* 设定后的mysql会在超时之后进行自动重新连接。但是,只有mysqli是支持mysqli_options高级设置的,
* 同时php限制mysqli_options函数不能对MYSQL_OPT_RECONNECT进行重新设置,这个选项只能在C Api中进行设置。
*****************************/
$this->_mysql_handler=mysqli_connect(
$this->_mysql_config[‘host’],
$this->_mysql_config[‘username’],
$this->_mysql_config[‘password’],
$this->_mysql_config[‘dbname’],
$this->_mysql_config[‘port’]
);
mysqli_set_charset($this->_mysql_handler,$this->_mysql_config[‘charset’]);
/***—————end————–***/
//无限循环监听队列
while(true)
{
/*非主要程序省略*/
/**
* 获取队列内容
*/
$article_id=$redis->rPop(‘article_search’);
if(empty($article_id) || !is_numeric($article_id)) continue;
/****************************
* mysql_ping()
****************************/
if(!mysqli_ping($this->_mysql_handler))
{
mysqli_close($this->_mysql_handler);
$this->_mysql_handler=mysqli_connect(
$this->_mysql_config[‘host’],
$this->_mysql_config[‘username’],
$this->_mysql_config[‘password’],
$this->_mysql_config[‘dbname’],
$this->_mysql_config[‘port’]
);
mysqli_set_charset($this->_mysql_handler,$this->_mysql_config[‘charset’]);
}
/***———-end———–***/
/**
* 获取文章信息
*/
$sql= “select * from article where article_id = {$article_id}”; //查询SQL
/**
* 执行查询
*/
if (!$query_result=mysqli_query($this->_mysql_handler,$sql))
{
$update->addDeleteById($article_id);
continue;
}
/**
* 获取查询结果
*/
$article=mysqli_fetch_array($query_result,MYSQLI_ASSOC);
if(empty($article) ||$article[‘is_deny’]==0)
{
$update->addDeleteById($article_id);
continue;
}
/***———–数据预处理完毕———–***/
补充知识:
MySQL server has gone away错误的两种原因:
1、可能是发送的SQL语句太长,以致超过了max_allowed_packet的大小,如果是这种原因,你只要修改my.cnf,加大max_allowed_packet的值即可。
2、可能是因为某些原因导致超时,比如说程序中获取数据库连接时采用了Singleton的做法,虽然多次连接数据库,但其实使用的都是同一个连接,而且程序中某两次操作数据库的间隔时间超过了wait_timeout(mysql命令行执行show global variables like ‘wait_timeout’可以查看),那么就可能出现问题。
第一种原因不再分析。
第二种连接超时的问题,采用加大wait_timeout的值不能够解决无限循环监测的问题,因为wait_timeout总是有一个时间上限的。事实上,mysql本身有一个自动重连机制,可以在mysql连接初始化后,通过mysql_options($link,MYSQL_OPT_RECONNECT,1)函数设定启用自动重连,即当mysql_query执行完毕如果间隔时间超过wait_timeout设定值,mysql连接断开然后自动重新连接,这样就实现了mysql长连接。
但是,php只有mysqli DB API具有mysqli_options()功能函数,其他DB API不支持该操作,并且,mysqli_options()函数又屏蔽了MYSQL_OPT_RECONNECT选项的设定权限,也就是说php不支持mysql自动重连机制的设定,而只有C API可以使用mysql_options(&mysql,MYSQL_OPT_RECONNECT,1)函数对自动重连进行设置。
那么,php要处理类似文章开头的mysql长连接场景,貌似就只能采用mysql_ping()的办法来处理了,不过,这就足够了。