PHP ZF2 Mysql 已经消失

PHP ZF2 Mysql has gone away

您好,我有一个 php 守护进程可以处理来自 rabbitmq

的请求

一天后,由于错误 MySQL 已消失,无法再执行。

PHP Warning:  PDOStatement::execute(): MySQL server has gone away in /var/www/daemon/www/vendor/zendframework/zendframework/library/Zend/Db/Adapter/Driver/Pdo/Statement.php on line 239
PHP Warning:  PDOStatement::execute(): Error reading result set\'s header in /var/www/daemon/www/vendor/zendframework/zendframework/library/Zend/Db/Adapter/Driver/Pdo/Statement.php on line 239

我没有使用学说,而是将我的 \Zend\Db\Adapter\Adapter 发送到具有以下功能的数据库包装器 class。

public static function executeScalar($statement, $parameters, \Zend\Db\Adapter\Adapter $dbAdapter)
{
    $dbResult = new DbResult();
    if (! $statement) {
        $dbResult->addError('No statement given');
        return $dbResult;
    }

    $stmt = $dbAdapter->createStatement();
    $stmt->prepare($statement);

    foreach ($parameters as $key => &$param) {
        $stmt->getResource()->bindParam($key + 1, $param[0], $param[1]);
    }

    try {
        $result = $stmt->execute();
        $dbResult->setResult($result);
    } catch (\Zend\Db\Adapter\ExceptionInterface $e) {
        $dbResult->addError('DB Error');
        $message = $e->getPrevious() ? $e->getPrevious()->getMessage() : $e->getMessage();
        $dbResult->addError($message);
    } catch (\Zend\Db\Adapter\Exception $e) {
        $dbResult->addError('DB Error');
        $dbResult->addError($e->getMessage());
    } catch (\PDOException $e) {
        $dbResult->addError('DB Error');
        $dbResult->addError($e->getMessage());
    } catch (\Exception $e) {
        $dbResult->addError('DB Error');
        $dbResult->addError($e->getMessage());
    }
    $stmt->getResource()->closeCursor();

    return $dbResult;
}

DbResult 是我自己的db result wrapper class 它主要检查return 是否为空,错误是什么,有多少行等

这是我的 database.local.php 配置

return array(
    'service_manager' => array(
    'factories' => array(
        'mysql' => function ($sm)
        {
            return new Zend\Db\Adapter\Adapter(array(
                'driver' => 'PdoMysql',
                'hostname' => 'localhost',
                'database' => 'daemon',
                'username' => 'daemon',
                'password' => 'password',
                'driver_options' => array(
                    \PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\'',
                    \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
                    \PDO::ATTR_EMULATE_PREPARES => true,
                    \PDO::MYSQL_ATTR_LOCAL_INFILE => true
                )
            ));
        },
    )
    )
)

所以每次我想执行 sql 时,我都会在控制器或任何其他 class 中执行此操作(仅举个例子)

$service = $this->getServiceLocator();
$dbAdapter = $service->get('mysql');
$get = \Db\Database::executeScalar('SELECT * FROM mytable WHERE id <= ?', array(10), $dbAdapter); 

我似乎无法捕捉到警告,有没有办法强制重新连接,或者我可能只是在每次请求后断开连接?

这可以处理错误吗? 对于每个新请求,我都会这样做

$dbAdapter->getDriver()->getConnection()->connect();

在请求结束时我这样做

$dbAdapter->getDriver()->getConnection()->disconnect();

您可以创建与数据库的持久连接,但请注意,创建持久连接不应该是第一个要寻找的解决方案。在尝试之前一定要对该主题进行一些研究。您可以在此处找到一些文档:

http://php.net/manual/en/pdo.connections.php#example-954

另一方面,您应该查找已发送的查询,以便消息消失的原因不是由 mysql 服务器收到的数据包太大(例如:插入大 blob)引起的。因为如果是,连接仍然会意外关闭。

是的,我检查了持久连接选项,但我也不喜欢它。

我发现了问题,它是由 mysql 服务器在 'wait timeout' 之后关闭空闲连接引起的。当 mysql 关闭空闲连接时,PDO 将不会收到任何事件,因此下次您发起查询时它会 return Mysql has gone away 错误。

对于 http 请求,这是可以接受的,因为在服务器响应请求后,它将 stop/exit php 执行关闭与数据库的所有连接。

对于 daemon/service 这是不可接受的,因为它大部分时间都在等待客户端请求(空闲)。我的解决方案是每次完成处理客户端请求时关闭连接。例如:

while (true) {
    //listen to rabbitmq queue
    //...

    //do something base on client request from rabbitmq queue
    //...

    //close the connection whether it use database or not
    //connection will be reconnected when we call $service->get('mysql');
    $service = $this->getServiceLocator();
    $dbAdapter = $service->get('mysql');
    $dbAdapter->getDriver()->getConnection()->disconnect();
}