web-dev-qa-db-fra.com

AOP: le serveur MySQL est parti

J'ai un script qui fait beaucoup de travail de nuit tous les soirs.

Il utilise une instruction préparée par PDO qui s'exécute en boucle.

Les premiers fonctionnent bien, mais j'arrive à un point où ils échouent tous avec l'erreur: "Le serveur MySQL est parti".

Nous exécutons MySQL 5.0.77.

PHP version 5.2.12

Le reste du site fonctionne bien.

42
Nathan H

La section B.5.2.9. Le serveur MySQL a dispar du manuel MySQL contient une liste des causes possibles de cette erreur.

Peut-être êtes-vous dans une de ces situations? - Surtout si l'on considère que vous exécutez une longue opération, le point concernant wait_timeout pourrait être intéressant ...

29
Pascal MARTIN

Vous avez probablement envoyé au serveur un paquet plus long que le paquet maximum autorisé.

Lorsque vous essayez d'insérer un BLOB qui dépasse la taille de paquet maximale de votre serveur, même sur un serveur local, vous verrez le message d'erreur suivant sur le côté client:

Le serveur MySQL est parti

Et le message d'erreur suivant dans le journal du serveur: (si la journalisation des erreurs est activée)

Erreur 1153 Vous avez un paquet plus grand que les octets "max_allowed_packet"

Pour résoudre ce problème, vous devez décider quelle est la taille du plus grand BLOB que vous insérerez et définir max_allowed_packet dans my.ini en conséquence, par exemple:

[mysqld]
...
max_allowed_packet = 200M
...
24
rustyx

J'ai eu le même problème où l'administration du serveur d'hébergement tue la connexion s'il y a un délai d'attente.

Puisque j'ai utilisé la requête en grande partie, j'ai écrit un code qui, au lieu d'utiliser la classe PDO, nous pouvons inclure la classe ci-dessous et remplacer le nom de classe par "ConnectionManagerPDO". Je viens de terminer la classe PDO.

final class ConnectionManagerPDO
{

    private $dsn;
    private $username;
    private $passwd;
    private $options;
    private $db;
    private $shouldReconnect;

    const RETRY_ATTEMPTS = 3;

    public function __construct($dsn, $username, $passwd, $options = array())
    {
        $this->dsn = $dsn;
        $this->username = $username;
        $this->passwd = $passwd;
        $this->options = $options;
        $this->shouldReconnect = true;
        try {
            $this->connect();
        } catch (PDOException $e) {
            throw $e;
        }
    }

    /**
     * @param $method
     * @param $args
     * @return mixed
     * @throws Exception
     * @throws PDOException
     */
    public function __call($method, $args)
    {
        $has_gone_away = false;
        $retry_attempt = 0;
        try_again:
        try {

            if (is_callable(array($this->db, $method))) {

                return call_user_func_array(array($this->db, $method), $args);
            } else {

                trigger_error("Call to undefined method '{$method}'");
                /*
                 * or
                 *
                 * throw new Exception("Call to undefined method.");
                 *
                 */
            }
        } catch (\PDOException $e) {

            $exception_message = $e->getMessage();

            if (
                ($this->shouldReconnect)
                && strpos($exception_message, 'server has gone away') !== false
                && $retry_attempt <= self::RETRY_ATTEMPTS
            ) {
                $has_gone_away = true;
            } else {
                /*
                 * What are you going to do with it... Throw it back.. FIRE IN THE HOLE
                 */
                throw $e;
            }
        }

        if ($has_gone_away) {
            $retry_attempt++;
            $this->reconnect();
            goto try_again;
        }
    }


    /**
     * Connects to DB
     */
    private function connect()
    {
        $this->db = new PDO($this->dsn, $this->username, $this->passwd, $this->options);
        /*
         * I am manually setting to catch error as exception so that the connection lost can be handled.
         */
        $this->db->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
    }

    /**
     * Reconnects to DB
     */
    private function reconnect()
    {
        $this->db = null;
        $this->connect();
    }
}

Ensuite, l'utilisation peut commencer à utiliser la classe ci-dessus comme vous le faites dans PDO.

try {
    $db = new ConnectionManagerPDO("mysql:Host=localhost;dbname=dummy_test", "root", "");
    $query = $db->query("select * from test");
    $query->setFetchMode(PDO::FETCH_ASSOC);
}
catch(PDOException $e){
    /*
        handle the exception throw in ConnectionManagerPDO
    */
}
3
mysticmo

Il est probable que votre connexion a été interrompue (par exemple par wait_timeout ou un autre thread émettant une commande KILL), le serveur est tombé en panne ou vous avez violé le protocole mysql d'une manière ou d'une autre.

Ce dernier est susceptible d'être un bogue dans PDO, ce qui est extrêmement probable si vous utilisez des instructions préparées côté serveur ou des résultats multiples (indice: ne pas)

Une panne de serveur devra être étudiée; regardez les journaux du serveur.

Si vous ne savez toujours pas ce qui se passe, utilisez un videur de paquets réseau (par exemple tcpdump) pour vider le contenu de la connexion.

Vous pouvez également activer le journal général des requêtes, mais faites-le très soigneusement en production.

2
MarkR

Essayez d'utiliser PDO::setAttribute(PDO::ATTR_EMULATE_PREPARES, true) sur vos instances de pod. Je ne sais pas que cela aidera, mais sans données de journal, c'est tout ce que j'ai.

2
prodigitalson

Nathan H, ci-dessous est la classe php pour la reconnexion pdo + exemple d'utilisation du code. Capture d'écran est joint.

<?php

# set errors reporting level
error_reporting(E_ALL ^ E_NOTICE ^ E_WARNING);

# set pdo connection
include('db.connection.pdo.php');

/* # this is "db.connection.pdo.php" content
define('DB_Host', 'localhost');
define('DB_NAME', '');
define('DB_USER', '');
define('DB_PWD', '');
define('DB_PREFIX', '');
define('DB_SHOW_ERRORS', 1);

# connect to db
try {
    $dbh = new PDO('mysql:Host='.DB_Host.';dbname='.DB_NAME, DB_USER, DB_PWD);
    $dbh->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
    $dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, TRUE);
    $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
catch(PDOException $e) {
    # echo $e->getMessage()."<br />";
    # exit;
    exit("Site is temporary unavailable."); #
}
*/

$reconnection = new PDOReconnection($dbh);

$reconnection->getTimeout();

echo $dbh->query('select 1')->fetchColumn();
echo PHP_EOL;
echo 'sleep 10 seconds..'.PHP_EOL;

sleep(10);

$dbh = $reconnection->checkConnection();
echo $dbh->query('select 1')->fetchColumn();
echo PHP_EOL;
echo 'sleep 35 seconds..'.PHP_EOL;

sleep(35);

$dbh = $reconnection->checkConnection();
echo $dbh->query('select 1')->fetchColumn();
echo PHP_EOL;
echo 'sleep 55 seconds..'.PHP_EOL;

sleep(55);

$dbh = $reconnection->checkConnection();
echo $dbh->query('select 1')->fetchColumn();
echo PHP_EOL;

echo 'sleep 300 seconds..'.PHP_EOL;
sleep(300);
$dbh = $reconnection->checkConnection();
echo $dbh->query('select 1')->fetchColumn();
echo PHP_EOL;

# *************************************************************************************************
# Class for PDO reconnection
class PDOReconnection
{
    private $dbh;

    # constructor
    public function __construct($dbh)
    {
        $this->dbh = $dbh;
    }

    # *************************************************************************************************

    # get mysql variable "wait_timeout" value
    public function getTimeout()
    {
        $timeout = $this->dbh->query('show variables like "wait_timeout"')->fetch(); # print_r($timeout);
        echo '========================'.PHP_EOL.'mysql variable "wait_timeout": '.$timeout['Value'].' seconds.'.PHP_EOL.'========================'.PHP_EOL;
    }

    # *************************************************************************************************

    # check mysql connection
    public function checkConnection()
    {
        try {
            $this->dbh->query('select 1')->fetchColumn();
            echo 'old connection works..'.PHP_EOL.'========================'.PHP_EOL;
        } catch (PDOException $Exception) {
            # echo 'there is no connection.'.PHP_EOL;
            $this->dbh = $this->reconnect();
            echo 'connection was lost, reconnect..'.PHP_EOL.'========================'.PHP_EOL;
        }

        return $this->dbh;
    }

    # *************************************************************************************************

    # reconnect to mysql
    public function reconnect()
    {
        $dbh = new PDO('mysql:Host=' . DB_Host . ';dbname=' . DB_NAME, DB_USER, DB_PWD);
        $dbh->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
        $dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, TRUE); 
        $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);    
        return $dbh;
    }
}
# /Class for PDO reconnection
# *************************************************************************************************
2
web.developer

J'ai eu exactement le même problème. J'ai résolu ce problème en effectuant unset sur l'objet PDO au lieu de le définir sur NULL.

Par exemple:

function connectdb($dsn,$username,$password,$driver_options) {
    try {
        $dbh = new PDO($dsn,$username,$password,$driver_options);
        return $dbh;
    }
    catch(PDOException $e)
    {
        print "DB Error: ".$e->getMessage()."<br />";
        die();
    }

}

function closedb($dbh) {
    unset($dbh);             // use this line instead of $dbh = NULL;
}

En outre, il est fortement recommandé de désactiver tous vos objets PDO. Cela inclut les variables qui contiennent des instructions préparées.

0
flang3r