场景:采用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()的办法来处理了,不过,这就足够了。