mysql の wait_timeout を超えると、PHP CLI スクリプトで接続が失われます。wait_timeout を変更できないので、PDOStatement を使用してクエリを実行するときに再接続する try/catch ステートメントを作成するにはどうすればよいでしょうか?
3 に答える
エラー後の DB への再接続は、実際には、最初に思われるよりもはるかに複雑な問題です。
私の最初のアイデアは、メソッドを内部 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() メソッドがあきらめる前に数回の再試行を行い、一時的なエラーの後の再接続を可能にします (これはデモンストレーション用であり、再試行間のバックオフなど、実際の実装に必要ないくつかの機能が欠けています。適切なエラー ログなど)。このアプローチでは、MySQL 構文エラーなどによって接続がリセットされて再試行が試行されることを望まないという理由で、スローされた PDO 例外の詳細を確認する必要もあります。「サーバーがなくなった」などの場合にのみ発生させたいと考えています。
おわかりのように、すべてのプロキシされた PDO メソッドを実装するのは大変な作業になります。
しかし、もっと大きな問題があります。これは、PDO だけでなく、データベースと通信するすべてのコードに共通の問題です。トランザクションの途中で接続が失われた場合はどうなりますか? 最後のコミットまでに行ったすべての作業が失われ、再開しても論理的な意味がなくなる可能性があるため、その場合、スクリプトを再接続して中断したところから再開することは望ましくありません。再接続した後、最初からやり直す必要があります。したがって、おそらくスクリプト全体を最初からやり直したいだけで、再接続を試みても意味がありません。これがおそらく、mySQLI が再接続をサポートしているのに PDO がサポートしていない理由です。
スクリプトが読み取りまたは非トランザクション書き込みのみを行う場合でも、上記のアプローチには価値がありますが、トランザクションをミックスに投入するとすぐに、再接続を試みない方が実際にははるかに優れています。
最善の方法は、PDO インスタンスの作成を、インスタンスと作成時刻の両方を格納するシングルトン (つまり、MyPDOFactory) にラップすることです。そうすることで、TTL に達した後 (2 または 3 秒が経過した後)、再利用または再作成できます。ほとんどのアプリケーションで十分です)。MyPDOFactory::get() を呼び出して、PDOStatement の準備に使用できる有効な PDO を取得する必要があります。できるだけ早く実行するようにしてください。
これはあなたを助けることができると思います。
/* 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;