Как восстановить соединение с MySQL через PDO в PHP-скрипте?

На сервере под Windows Server 2003 работает PHP-скрипт в режиме CLI (что-то вроде демона), он принимает данные и записывает их в базу.

Соединение с базой устанавливается через PDO с помощью самописного класса:
class DB {
    protected static $dbInstance;

    private function __construct() {
        return self;
    }
    private function __clone() {
        return self;
    }

    private function __wakeup() {
        return self;
    }

    public static function getDB () {
        if (is_null(self::$dbInstance)) {
            try {
                $cfg = Config::getConfig();
                self::$dbInstance = new PDO(
                    'mysql:host='.$cfg['dbHost'].';dbname='.$cfg['dbName'].''
                    , $cfg['dbUser']
                    , $cfg['dbPass']);
            }
            catch (PDOException $e) {
                return false;
            }
        }
        return self::$dbInstance;
    }
}


Когда данные идут непрерывно, соединение держится отлично. Но при простое скрипта более 17 часов (может быть и меньше) - данные перестают записываться. Проверка соединения на null результатов не принесла. Могу предположить, что надо как-то проверять состояние соединения.

Вопрос: как проверить наличие соединения с MySQL через PDO?
  • Вопрос задан
  • 5306 просмотров
Решения вопроса 3
ScorpLeX
@ScorpLeX
На сколько я помню, там нет функции пинг.
Можно воспользоваться чем то таким http://terenceyim.wordpress.com/2009/01/09/adding-ping-function-to-pdo/ или извратится по своему.
Ответ написан
Opaspap
@Opaspap
что то типа

private $lastping=0;
public function ping() {
    if ((time()-$this->lastping)<10) return;
    try {
        self::$dbInstance->query('SELECT 1');
    } catch {
       self::$dbInstance=NULL;
       self::getDB();
    }
    $this->lastping=time();
}


Это если ping нужен, я как понимаю у вас standalone приложение, можно наверное этот пинг заставить не каждый раз ping делать, а скажем не чаще раз в 10 сек, чтобы когда много подряд запросов не проверять, а ловить уже исключения (если уж порвалось соединение по причине остановки сервера, например)

upd: реализовал что написал выше, т.к. заметил ответ @ScorpLeX
Ответ написан
Remmi
@Remmi Автор вопроса
Спасибо @ScorpLeX и @Opaspap за верное направление.

Решил задачу следующим образом: написал функцию пинга, но не просто отправлял запрос "SELECT 1" к базе, а запрос с простым арифметическим выражением и проверял его результат. Без проверки показывало, что запрос выполняется корректно, а на самом деле это было не так (подозреваю, что надо было включать исключения четвёртым параметром).

/**
     * Проверка состояния соединения: активно или нет
     * @return bool
     */
    public static function isActiveConnection () {
        if (is_null(self::$dbInstance)) {            
            return false;
        }

        try {
            $testRes = self::$dbInstance->query('SELECT 1+2+3');
            $testArray = $testRes->fetch();
            if ($testArray[0] == 6) {
                return true;
            }
        }
        catch (PDOException $e) {
            return false;
        }
        return false;
    }

    /**
     * Возвращает объект базы данных для дальнейшей работы
     * @return mixed
     */
    public static function getDB () {
        if (!self::isActiveConnection()) {
            self::initConnection();    
        }
        return self::$dbInstance;
    }
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 4
ertaquo
@ertaquo
В конструкторе PDO четвертым параметром передавайте array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION), тогда вроде должны генерироваться исключения.
Ответ написан
Комментировать
bredmm
@bredmm
если соединение с базой именно обрывается то всегда вернется ошибка "MySQL server has gone away" от mysql клиента на сервере, можно отлавливать ее
Ответ написан
Комментировать
toxa82
@toxa82
Самый простой способ установить переменную MySQL wait_timeout по-больше. Но если окажется слишком много висящих соединений можно упереться в лимит коннектов.
Ответ написан
thestump
@thestump
программист PHP
Лучше не терять соединение:
$this -> pdo = new \PDO(
                "mysql:host=" . $this -> DB_HOST . " ;dbname=" . $this -> DB_NAME . ";", 
                $this -> DB_USER, 
                $this -> DB_PW,
                array(\PDO::ATTR_PERSISTENT => true)
            );
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы