2010-02-09 21 views
31

Tengo un script que hace muchas tareas nocturnas.PDO: el servidor MySQL se ha ido

Utiliza una declaración preparada PDO que se ejecuta en un bucle.

Los primeros están funcionando bien, pero luego llegué a un punto donde todos fallaron con el error: "El servidor MySQL se ha ido".

Ejecutamos MySQL 5.0.77.

PHP Versión 5.2.12

El resto del sitio funciona muy bien.

+0

¿Qué dicen los registros de mysql? – prodigitalson

+0

uhg no registramos mysql actualmente porque tenemos demasiado sucediendo –

+0

Bueno, es un poco difícil de solucionar en ese caso, ya que es un problema con mysql no necesariamente PDO/PHP. Lo pondría a ejecutar un lote de muestra de lo que se ejecuta durante la noche y luego revisaré los registros :-) – prodigitalson

Respuesta

23

La sección B.5.2.9. MySQL server has gone away del manual de MySQL tiene una lista de posibles causas de este error.

¿Quizás estás en una de esas situaciones? - Especialmente considerando que está ejecutando una operación larga, el punto sobre wait_timeout podría ser interesante ...

+0

Creo que tienes razón. mi tiempo de espera de espera es de 90 segundos y mi código puede tardar un tiempo antes de ejecutarse. ¿Hay alguna forma de cambiar dinámicamente a través de PHP solo para 1 script? –

+4

o una forma de "hacer ping" a mysql a través de php PDO? –

+0

A juzgar por el documento, la forma más simple parece simplemente reconectarse; es decir, si obtiene ese error, volver a instanciar su clase PDO podría funcionar –

2

Pruebe usar PDO::setAttribute(PDO::ATTR_EMULATE_PREPARES, true) en su (s) instancia (s) de módulo (s). No sé si será de ayuda, pero sin datos de registro, todo lo que obtuve.

2

Es probable que su conexión haya sido eliminada (por ejemplo, por wait_timeout u otro hilo que emita un comando KILL), el servidor se haya bloqueado o haya violado el protocolo mysql de alguna manera.

Este último es probable que sea un error en la DOP, que es muy probable que si usted está utilizando comandos preparados en el servidor o multi-resultados (pista: no lo hacen)

Una caída del servidor necesitará ser investigado; mira los registros del servidor.

Si aún no sabe lo que está pasando, utilice un volcador de paquetes de red (por ejemplo, tcpdump) para volcar el contenido de la conexión.

También puede habilitar el registro general de consultas, pero hágalo con mucho cuidado en la producción.

15

Lo más probable es que envíe un paquete al servidor que sea más largo que el paquete máximo permitido.

Cuando intente insertar un BLOB que exceda el tamaño máximo de paquete de su servidor, incluso en un servidor local verá "El servidor MySQL desapareció" en el lado del cliente, y "Error 1153 Obtuvo un paquete mayor que 'max_allowed_packet 'bytes' en el registro del servidor (si el registro de errores está habilitado). Para solucionar este problema tiene que decidir cuál es el tamaño del BLOB más grande que jamás insertar y fijar max_allowed_packet en my.ini en consecuencia, por ejemplo:

[mysqld] 
... 
max_allowed_packet = 200M 
... 
+0

Gracias. Configuración max_allowed_packet = 16M solucionó mi problema –

+1

+1 Esto solucionó un error que estaba recibiendo (diciendo que el servidor también se había ido) – Nate

+0

Lo hice en ' my.cnf' * en lugar de my.ini como mencionaste, pero sí que lo hizo. –

0

que tenía el mismo problema. Resolví este problema desactivando el objeto PDO en lugar de fijarlo en NULL.

Por ejemplo:

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; 
} 

Además, es muy recomendable para desarmar todos los objetos PDO. Eso incluye variables que contienen declaraciones preparadas.

+4

¿No estás desarmando la variable local dentro de 'closedb'? es decir, esa función no hace nada. '$ dbh = null' tampoco haría nada a menos que abofetees un' & 'en la firma de la función. – mpen

-3
$pdo = new PDO(
    $dsn, 
    $config['username'], 
    $config['password'], 
    array(
     PDO::ATTR_PERSISTENT => true, 
     PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION 
    ) 
); 

prueba esto.Puede funcionar

3

Tuve el mismo problema cuando la administración del servidor de host mata la conexión si hay un tiempo de espera.

Como he utilizado la consulta en la parte principal, escribí un código que, en lugar de utilizar la clase PDO, podemos incluir la clase siguiente y reemplazar el nombre de clase por "ConnectionManagerPDO". Acabo de envolver la clase 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(); 
    } 
} 

Luego el uso puede comenzar a usar la clase anterior como lo hace en 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 
    */ 
} 
+0

La reconexión automática no funcionó conmigo. Así que eliminé la función __call e hice pública la función de reconexión. Lo llamo cuando hago una consulta en un bloque try/catch. –

+0

Solo se volverá a conectar si pdo devuelve una excepción "el servidor se ha ido". ¿Puede decirme qué excepción se produce? – mysticmo

+0

Lo intenté de nuevo: la excepción fue "El servidor MySQL se ha ido" y sí hizo la reconexión, está bien. Pero la consulta que debería haberse ejecutado en el momento en que se produjo la excepción se perdió, así que tuve que intentar capturar la consulta para poder ejecutarla nuevamente después de la reconexión. –

1

Nathan H, a continuación es clase php para pdo reconexión + muestra el uso de código. Screenshot está conectado.

<?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 
# ************************************************************************************************* 
Cuestiones relacionadas