PDO с PDOStatement переподключается при ошибке «сервер mysql ушел» - PullRequest
10 голосов
/ 08 февраля 2012

Когда mysql's wait_timeout превышен, я теряю соединение с моим PHP CLI-скриптом. Я не могу изменить wait_timeout, так как можно создать оператор try / catch, который переподключается, когда я использую PDOStatement для выполнения своих запросов?

Ответы [ 3 ]

1 голос
/ 05 апреля 2016

Повторное подключение к БД после ошибки на самом деле является гораздо более сложной проблемой, чем может показаться на первый взгляд.

Моей первой идеей было написать простой класс-оболочку для PDO, который проксирует методы на внутреннем объекте PDO и сам может обрабатывать ошибки соединения:

class BetterPDO extends PDO 
{
    private $realPDO  = NULL;
    private $dsn      = "";
    private $username = "";
    private $password = "";
    private $options  = [];

    public function __construct ($dsn, $username = "", $password = "", $options = [])
    {
        $this -> dsn = $dsn;
        $this -> username = $username;
        $this -> password = $password;
        $this -> options = $options;
    }

    private function getRealPDO ()
    {
        if (is_null ($this -> realPDO))
        {
            $this -> realPDO = new PDO ($this -> dsn, $this -> username, $this -> password, $this -> options);
        }
        return $this -> realPDO;
    }

    // We're only implementing exec for brevity but you have to do this for all public methods of PDO
    public function exec ($sql)
    {
        $retries = 0;
        while (true)
        {
            try 
            {
                return $this -> getRealPDO () -> exec ($sql);
            }
            catch (PDOException $ex) 
            {
                $this -> realPDO = NULL;
                if (++$retries > 5)
                {
                    // We've passed our retry limit
                    throw $ex;
                }
            }
        }
    }
}

Поскольку этот класс расширяет PDO, он можетиспользоваться везде, где может использоваться универсальный класс PDO.

Как вы можете видеть, этот подход даст вам несколько попыток, прежде чем метод exec () откажется, что позволит переподключиться после временных ошибок (это только для демонстрации и не имеет некоторых функций, которые могут потребоваться реальной реализации,как откат между попытками, адекватное ведение журнала ошибок и т. д.).Этот подход также потребует, чтобы вы проверили специфику исключения PDO на том основании, что вы не хотите, чтобы такие вещи, как синтаксические ошибки MySQL, вызывали сброс соединения и попытку повторной попытки.Вы хотите, чтобы это происходило только с такими вещами, как «Сервер ушел».

Как вы также можете видеть, реализация всех методов PDO с прокси будет непростой задачей, хотя, поскольку вам придется делать это только один раз, вероятно, стоит приложить усилия для этого.

Однако существует гораздо более серьезная проблема, которая является универсальной проблемой для любого кода, который обращается к базе данных, а не только к PDO.Что происходит, если соединение теряется в середине транзакции?Вы не хотите, чтобы ваш сценарий восстанавливал соединение и выбирал, где он остановился, в этом случае, потому что вся работа, которую вы проделали до последнего коммита, будет потеряна, и есть вероятность, что логическое возобновление не будет иметь никакого смыслапосле повторного подключения вам придется начать все сначала.Поэтому вы, вероятно, просто захотите, чтобы весь сценарий начинался заново, и попытка переподключения не имела бы никакого смысла.Вероятно, поэтому mySQLI поддерживает переподключение, а PDO - нет.

Если ваш сценарий выполняет только чтение или нетранзакционную запись, то вышеупомянутый подход все еще имеет значение, но как только вы добавляете транзакции в микс, на самом деле гораздо лучше не пытаться переподключиться.

1 голос
/ 07 апреля 2013

Наилучший подход заключается в том, чтобы обернуть создание экземпляра PDO в одноэлементный объект (т. Е. MyPDOFactory), в котором хранится как экземпляр, так и время создания. Таким образом, вы можете использовать его повторно или воссоздать после достижения TTL (2 или 3 секунды более чем достаточно для большинства приложений). Вам просто нужно вызвать MyPDOFactory :: get (), чтобы получить действительный PDO, который вы можете использовать для подготовки PDOStatement, просто убедитесь, что вы выполняете его как можно скорее.

0 голосов
/ 13 декабря 2013

Я думаю, что это может вам помочь.

/* Your Database Name */
    $dbname = 'mydatabase';

    /* Your Database User Name and Passowrd */
    $username = 'root';
    $password = 'password';

    try {
      /* Establish the database connection */
      $conn = new PDO("mysql:host=localhost;dbname=$dbname", $username, $password);
      $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
   /*  your code goes here*/
 } catch(PDOException $e) {
        echo 'ERROR: ' . $e->getMessage();
    }
    //mysql_close($conn);

     $conn=null;
...