mysqli_ping(): MySQL server has gone away

PHP 错误信息

errno:2 errmsg:mysqli_ping(): MySQL server has gone away
errno:8 errmsg:mysqli_ping(): send of 5 bytes failed with errno=32 Broken pipe

错误级别代号php

常量 说明
2 E_WARNING 运行时警告 (非致命错误)。仅给出提示信息,可是脚本不会终止运行。
8 E_NOTICE 运行时通知。表示脚本遇到可能会表现为错误的状况,可是在能够正常运行的脚本里面也可能会有相似的通知。

缘由分析

mysqli_ping()方法是专门为 libmysql 这种旧式的数据库驱动而设计的,它跟如今新式的 mysqlnd 驱动已再也不兼容,因此才会报错。 html

从 PHP 5.3.0 开始,就引入了 mysqlnd 驱动,而且在 5.4.0 版本开始做为默认的 MySQL 数据库驱动。
因此,如今最新的 PHP 版本也是使用 mysqlnd 做为默认驱动。mysql

mysqli_ping() 方法的做用是检测当前 mysql 链接是否存活,若不存活则自动重连。不过官方有下面一段话sql

Note: The php.ini setting mysqli.reconnect is ignored by the mysqlnd driver, so automatic reconnection is never attempted.shell

这说明了对于 mysqlnd 驱动来讲,mysqli_ping() 已经不能再实现自动重连了。数据库

那咱们还可以用该方法来判断当前的 MySQL 链接是否存活吗?若是能,那在咱们实现单例模式时是有帮助的。
由于我只须要链接一次数据库,就能够自始至终都使用同一个数据库链接来操做数据库。函数

<?php
class DB 
{
    private static $link;
    
    public static function getLink()
    {
        if(empty(self::$link) || mysqli_ping(self::$link) === false) {
            self::$link = mysqli_connect("host", "username", "password", "dbname");
        }
        
        return self::$link;
    }
}

看起来上面的程序并无问题,可是为何就会出现上面两个报错呢?并且是因为调用 mysqli_ping() 而引发的错误。.net

errno:2 errmsg:mysqli_ping(): MySQL server has gone away
errno:8 errmsg:mysqli_ping(): send of 5 bytes failed with errno=32 Broken pipe

其实不管怎么说,这个方法也不该该报错才对的,返回 true 或者 false 来告诉咱们链接是否存活便可,这里直接报错不太好。设计

根据 Bug #52561Bug #53287 这两个反馈来看,官方也没有给出答案,只是说 mysqli_ping() 已不适用于 mysqlnd,若是必定要用该函数那只能配合 libmysql 驱动来用。code

缘由结论

实际上,若是 MySQL 链接还存活的状况下,使用 mysqli_ping() 去检测是不会报错的,一切正常。只有当 MySQL 主动断开了与 PHP 的链接后,再用该方法去检测时才会出现报错信息。

这种状况通常会出如今两次间隔时间较长的时间内,期间 MySQL 根据配置参数 wait_timeout 的阀值而断开了长时间没有再发送查询请求的链接。若此时再调用 mysqli_ping(self::$link) 就会出现 MySQL server has gone away 这样的报错信息。

此外,对于第二个错误即 send of 5 bytes failed with errno=32 Broken pipe 虽然没有找到确切的缘由,可是能够推测也是因为 MySQL 链接断开而形成的。由于这两个错误出现得颇有规律,并且都是在差很少的时间间隔内出现。

解决方法

为了减小这种报错信息的出现,咱们能够在 php 程序中实现自动重连,即在 MySQL 断开链接前,就自动实现从新链接或者避免再使用 mysqli_ping() 去检测。MySQL 会在何时断开链接,能够查看 my.ini 配置中的 wait_timeout 参数。

mysql> show global variables like '%timeout';
+-----------------------------+----------+
| Variable_name               | Value    |
|-----------------------------+----------|
| connect_timeout             | 10       |
| delayed_insert_timeout      | 300      |
| innodb_flush_log_at_timeout | 1        |
| innodb_lock_wait_timeout    | 50       |
| innodb_rollback_on_timeout  | OFF      |
| interactive_timeout         | 28800    |
| lock_wait_timeout           | 31536000 |
| net_read_timeout            | 30       |
| net_write_timeout           | 60       |
| slave_net_timeout           | 3600     |
| thread_pool_idle_timeout    | 60       |
| wait_timeout                | 1800     |
+-----------------------------+----------+

注意最后一项 wait_timeout 的值是 1800 秒,表示 MySQL 会把 30 分钟以上没有任何查询请求的链接给断开。

根据上面的阀值,咱们在程序中实现自动重连。

<?php
class DB 
{
    private static $link;
    
    public static function getLink()
    {
        if(empty(self::$link) || mysqli_ping(self::$link) === false) {
            self::$link = mysqli_connect("host", "username", "password", "dbname");
        }
        
        return self::$link;
    }
    
    public static function keepConnectionAlive(&$start)
    {
        $passed = time() - $start;
        
        if($passed > 1800)
        {
            $start = time();
            self::$link = null;
        }
    }
}

在 PHP 程序中使用(通常会在耗时的 woker 中使用)

$start = time();

while(true)
{
    $params = Queues::get();

    DB::keepConnectionAlive($start);
    $link = DB::getLink();
    
    //...处理业务逻辑
}

参考文献

相关文章
相关标签/搜索