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();
    
    //...處理業務邏輯
}

參考文獻

相關文章
相關標籤/搜索