web-dev-qa-db-ja.com

PDO:MySQLサーバーはなくなりました

毎晩多くのレッグワークを行うスクリプトがあります。

ループで実行されるPDO準備済みステートメントを使用します。

最初のいくつかは正常に動作していますが、「MySQLサーバーがなくなった」というエラーですべてが失敗するところに到達します。

MySQL 5.0.77を実行します。

PHPバージョン5.2.12

サイトの残りの部分は正常に動作します。

42
Nathan H

MySQLマニュアルの B.5.2.9。MySQLサーバーがなくなりました セクションには、このエラーの考えられる原因のリストがあります。

たぶん、あなたはそれらの状況の1つにいますか? -特に長い操作を実行していることを考慮すると、 wait_timeout は興味深いかもしれません...

29
Pascal MARTIN

最も可能性が高いのは、許可された最大パケットより長いパケットをサーバーに送信したことです。

ローカルサーバー上であっても、サーバーの最大パケットサイズを超えるBLOBを挿入しようとすると、クライアント側に次のエラーメッセージが表示されます。

MySQLサーバーがなくなった

サーバーログに次のエラーメッセージが表示されます:(エラーログが有効になっている場合)

エラー1153 'max_allowed_pa​​cket'バイトより大きいパケットを取得しました

これを修正するには、挿入する最大のBLOBのサイズを決定し、それに応じてmax_allowed_packetmy.iniに設定する必要があります。次に例を示します。

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

タイムアウトが発生すると、ホスティングサーバー管理が接続を切断するという同じ問題が発生しました。

主要部分でクエリを使用したため、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
    */
}
3
mysticmo

接続が(たとえば、wait_timeoutまたはKILLコマンドを発行する別のスレッドによって)強制終了されたか、サーバーがクラッシュしたか、何らかの方法でmysqlプロトコルに違反した可能性があります。

後者はPDOのバグである可能性が高く、サーバー側の準備済みステートメントまたは複数の結果を使用している場合は非常に可能性が高い(ヒント:しない)

サーバーのクラッシュを調査する必要があります。サーバーログを見てください。

それでも何が起こっているのかわからない場合は、ネットワークパケットダンパー(tcpdumpなど)を使用して、接続の内容をダンプします。

一般的なクエリログを有効にすることもできますが、実稼働環境では非常に慎重に行ってください。

2
MarkR

ポッドインスタンスでPDO::setAttribute(PDO::ATTR_EMULATE_PREPARES, true)を使用してみてください。それが役立つことを知らないが、ログデータなしでそのすべてを手に入れた。

2
prodigitalson

Nathan H、以下はpdo再接続+コード使用サンプルのphpクラスです。 スクリーンショット が添付されています。

<?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

私はまったく同じ問題を抱えていました。 NULLに設定するのではなく、PDOオブジェクトの設定を解除することでこの問題を解決しました。

例えば:

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オブジェクトの設定を解除することを強くお勧めします。これには、準備済みステートメントを含む変数が含まれます。

0
flang3r