当超过 mysql 的 wait_timeout 时,我的 PHP CLI 脚本失去了连接。我无法更改 wait_timeout,那么当我使用 PDOStatement 执行查询时,如何构建一个重新连接的 try/catch 语句?
最佳答案
在发生错误后重新连接到数据库实际上是一个比乍看起来要复杂得多的问题。
我的第一个想法是为 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() 方法放弃之前为您提供几次重试,允许在 transient 错误后重新连接(这仅用于演示,缺少实际实现所需的一些功能,如退避)在重试之间、充分的错误记录等)。这种方法还需要您检查抛出的 PDO 异常的细节,因为您不希望 MySQL 语法错误之类的事情导致连接重置并尝试重试。您只希望它发生在诸如“服务器已消失”之类的事情上。
正如您还看到的那样,实现所有代理的 PDO 方法将变得很繁琐,但因为您只需要执行一次它可能值得为此付出努力。
但是还有一个更大的问题,这几乎是所有与数据库对话的代码的普遍问题,而不仅仅是 PDO。如果连接在交易过程中丢失会怎样?在这种情况下,您不希望您的脚本重新连接并从中断的地方继续,因为您在上次提交之前所做的所有工作都将丢失,并且很可能恢复没有逻辑意义重新连接后,您必须重新开始。因此,您可能只希望整个脚本重新开始,而尝试重新连接是没有任何意义的。这可能就是为什么 mySQLI 支持重新连接而 PDO 不支持的原因。
如果您的脚本只进行读取或非事务性写入,那么上述方法仍然有值(value),但是一旦您将事务投入其中,您实际上最好不要尝试重新连接。
关于php - 带有 PDOStatement 的 PDO 在出现 "mysql server gone"错误时重新连接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9190130/