У меня есть сценарий, который выполняет много ночной работы в ночное время.
Он использует подготовленный PDO оператор, который выполняется в цикле.
Первые несколько работают нормально, но потом я добираюсь до точки, где все они терпят неудачу с ошибкой: «Сервер MySQL ушел».
Мы запускаем MySQL 5.0.77.
Версия PHP 5.2.12
Остальная часть сайта работает нормально.
В.5.2.9. В сервере MySQL ушел раздел руководства MySQL содержит список возможных причин этой ошибки.
Может быть, вы находитесь в одной из таких ситуаций? – Особенно учитывая, что вы выполняете длительную операцию, точка wait_timeout
может быть интересной …
Скорее всего, вы отправили пакет на сервер, длина которого превышает максимально допустимый пакет.
Когда вы пытаетесь вставить BLOB, который превышает максимальный размер пакета вашего сервера, даже на локальном сервере вы увидите, что «сервер MySQL ушел» на стороне клиента, и «Ошибка 1153» Получил пакет, превышающий «max_allowed_packet» байты в журнал сервера (если включена регистрация ошибок). Чтобы исправить это, вам нужно решить, что такое размер самого большого BLOB, который вы когда-либо вставляете, и установите max_allowed_packet
в my.ini
соответственно, например:
[mysqld] ... max_allowed_packet = 200M ...
У меня была та же проблема, когда администрация сервера хостинга убивает соединение, если есть тайм-аут.
Поскольку я использовал запрос в большей части, я написал код, который вместо использования класса PDO мы можем включить ниже класс и заменить имя класса на «ConnectionManagerPDO». Я просто завернул класс 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(); } }
Тогда использование может начать использовать вышеуказанный класс, как и в 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 */ }
Попробуйте использовать PDO::setAttribute(PDO::ATTR_EMULATE_PREPARES, true)
на экземплярах вашего модуля. Не знаю, что это поможет, но без данных журнала все, что я получил.
Вероятно, что ваше соединение было убито (например, wait_timeout или другой поток, выдающий команду KILL), сервер разбился или вы каким-то образом нарушили протокол mysql.
Последнее, вероятно, будет ошибкой в PDO, что весьма вероятно, если вы используете подготовленные на сервере заявления или многорезультаты (подсказка: Do not)
Крушение сервера необходимо будет исследовать; посмотрите журналы сервера.
Если вы все еще не знаете, что происходит, используйте сетевой упаковщик (например, tcpdump), чтобы выгрузить содержимое соединения.
Вы также можете включить общий журнал запросов – но делайте это очень осторожно в процессе производства.
Nathan H, ниже – это php-класс для повторного подключения pdo + пример использования кода. Скриншот прилагается.
<?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 # *************************************************************************************************
в<?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 # *************************************************************************************************
.<?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 # *************************************************************************************************
У меня была точно такая же проблема. Я решил эту проблему, сделав unset в объекте PDO вместо того, чтобы установить его в NULL.
Например:
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; }
с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; }
Кроме того, настоятельно рекомендуется отключить все ваши объекты PDO. Это включает переменные, содержащие подготовленные операторы.
$pdo = new PDO( $dsn, $config['username'], $config['password'], array( PDO::ATTR_PERSISTENT => true, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION ) );
попробуй это. Он может работать