带有 PDOStatement 的 PDO 重新连接“mysql 服务器消失”错误

Posted

技术标签:

【中文标题】带有 PDOStatement 的 PDO 重新连接“mysql 服务器消失”错误【英文标题】:PDO with PDOStatement reconnect on "mysql server gone" error 【发布时间】:2012-02-29 16:47:51 【问题描述】:

当超过 mysql 的 wait_timeout 时,我的 php CLI 脚本失去了连接。我无法更改 wait_timeout,那么当我使用 PDOStatement 执行查询时,如何构建一个重新连接的 try/catch 语句?

【问题讨论】:

【参考方案1】:

最好的方法是将 PDO 实例创建包装到一个单例(即 MyPDOFactory)中,该单例存储实例和创建时间,这样,您可以重用它或在达到 TTL 后重新创建它(2 或对于大多数应用程序来说,3 秒就足够了)。你只需要调用 MyPDOFactory::get() 来获得一个有效的 PDO,你可以使用它来准备 PDOStatement,只要确保你尽快执行它。

【讨论】:

我理解你的想法,但你真的应该在你的答案中添加代码。 如果您正在运行一组查询 a) 事务性,并且 b) 发生的时间恰好比 TTL 长?【参考方案2】:

我认为这可以帮助你。

/* 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;

【讨论】:

您的回答对于超时后重新连接没有用。在 try 块中,您应该放置查询代码,如果连接消失,请在 catch 块中重新连接。然后以某种方式再次启动查询...【参考方案3】:

发生错误后重新连接数据库实际上是一个比最初看起来要复杂得多的问题。

我的第一个想法是为 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 不支持的原因。

如果您的脚本只进行读取或非事务性写入,那么上述方法仍然有价值,但是一旦您将事务放入混合中,您实际上最好不要尝试重新连接。

【讨论】:

也可以在代理的PDO类中实现魔术__call方法

以上是关于带有 PDOStatement 的 PDO 重新连接“mysql 服务器消失”错误的主要内容,如果未能解决你的问题,请参考以下文章

PHP扩展PDO MySQL之PDOStatement::bindParam vs bindValue

PDO中的预处理

PDOStatement 到 JSON [关闭]

PHP使用PDOStatement处理结果集

PHP PDO学习小结

PDO类PDOPDOStatementPDOException