PDO 关闭连接

Posted

技术标签:

【中文标题】PDO 关闭连接【英文标题】:PDO closing connection 【发布时间】:2013-08-19 02:11:36 【问题描述】:

mysqli 相比,PDO 只是一个相当简单的问题。

使用 MySQLi,您可以关闭连接:

$this->connection->close();

但是,对于 PDO,它声明您使用以下方式打开连接:

$this->connection = new PDO();

但要关闭连接,请将其设置为 null

$this->connection = null;

这是正确的吗?这真的会释放 PDO 连接吗? (我知道它设置为null。)我的意思是对于MySQLi,你必须调用一个函数(close)来关闭连接。 PDO 是否像= null 一样容易断开连接?还是有关闭连接的功能?

【问题讨论】:

我问的原因是我不确定我是否正确关闭了连接。但不是真的很感兴趣 当您的 php 脚本停止执行时,数据库连接会自动关闭。 如果你用完了它,为什么不继续并终止它,特别是当你完成与数据库的交互后有一些耗时的代码时。不过,我并没有真正看到等待脚本完成的问题(除了减少与数据库服务器的连接。) github.com/php/php-src/blob/master/ext/pdo/pdo_dbh.c 自己了解它是如何工作的:P 并非所有的 php 脚本都是短暂的。那里有 php 守护进程。我认为这是一个很好的个人澄清。 【参考方案1】:

根据文档,您是正确的 (http://php.net/manual/en/pdo.connections.php):

连接在该 PDO 对象的生命周期内保持活动。到 关闭连接,您需要销毁对象,确保 对它的所有剩余引用都将被删除——你可以通过分配 保存对象的变量为 NULL。如果你不这样做 明确地,PHP 将自动关闭连接,当您 脚本结束

请注意,如果您将 PDO 对象初始化为持久连接,它不会自动关闭连接。

【讨论】:

如果我有一个没有结束的进程怎么办?例如网络套接字。有没有办法不使用持久连接? 对于长时间运行的脚本中的持久连接,您可以故意(或意外)因超时(例如在 my.ini 中)或由于许多其他原因而终止连接。连接或运行查询时,捕获任何错误,如果是“MySQL 已消失”,请尝试再次连接或再次运行查询。 Note that if you initialise the PDO object as a persistent connection it will not automatically close the connection 但是如果一个连接是持久的,并且我在脚本结束之前显式调用了NULL,即使它是持久的,它也会被关闭,对吗? @tonix 不,它应该被释放(提供给另一个脚本),但不能关闭。 @tonix 我想​​是的,是的。来自PHP manual on persistent connections 的引用:"警告 使用持久连接时需要牢记一些额外的注意事项。一个是在持久连接上使用表锁定时,如果脚本由于某种原因无法释放锁定,那么使用同一连接的后续脚本将无限期阻塞,并且可能需要您重新启动 httpd 服务器或数据库服务器。”【参考方案2】:
$conn=new PDO("mysql:host=$host;dbname=$dbname",$user,$pass);
    // If this is your connection then you have to assign null
    // to your connection variable as follows:
$conn=null;
    // By this way you can close connection in PDO.

【讨论】:

恕我直言,我认为这是一个非常糟糕的模式,尤其是当开发人员可能存储多个 pdo 引用副本时。 $a = 新 PDO(...); $b = $a; $a = 空;在那里,您的 PDO 对象将永远保持打开状态(在类似守护进程的 php 程序中)。当 PDO 引用跨越函数和对象属性时尤其如此,并且您永远无法确定将它们全部归零。 PDO上应该有一个->close()方法。 不喜欢 PDO 的另一个原因。 @Gabriel - 我建议“存储多个副本”是一种更糟糕的模式。 如果您在这两行之间创建了一个 PDOStatement 对象(即在每种实际情况下),这将不起作用。要关闭连接,您必须将 PDO 对象和 PDOStatement 对象都设置为 null。见这里:php.net/manual/en/pdo.connections.php#114822【参考方案3】:

我创建了一个派生类来拥有更自文档化的指令,而不是 $conn=null;

class CMyPDO extends PDO 
    public function __construct($dsn, $username = null, $password = null, array $options = null) 
        parent::__construct($dsn, $username, $password, $options);
    

    static function getNewConnection() 
        $conn=null;
        try 
            $conn = new CMyPDO("mysql:host=$host;dbname=$dbname",$user,$pass);
        
        catch (PDOException $exc) 
            echo $exc->getMessage();
        
        return $conn;
    

    static function closeConnection(&$conn) 
        $conn=null;
    

所以我可以在以下之间调用我的代码:

$conn=CMyPDO::getNewConnection();
// my code
CMyPDO::closeConnection($conn);

【讨论】:

您可以将 CMyPDO::__construct() 方法设为私有并在那里使用单例模式.. 是的,这是可能的。如果您一次使用多个数据库,您还需要通过另一种方法分配连接信息。差别很小,只是您调用实例方法的指令稍长。 @AdityaHajare 您不能在子类中将超类的公共方法设为私有.. @nickdnk,你是对的。我的意思是创建一个独立的类 CMyPDO(不使其扩展 PDO),然后在 CMyPDO 的私有构造函数(new PDO($dsn, $dbuser, $dbpass);) 类中创建一个数据库实例,确保只有一个实例在整个应用程序中都可用(单例设计模式)。 @Fil 但是代码“外部”closeConnection 不应该知道它需要复制对变量的引用而不是分配对象。换句话说,您尝试编写关闭 PDO 函数的方式会产生不良的副作用,使其不可靠。这样做的唯一方法是closeConnection 检查代码中存在多少对 PDO 对象的引用,并在存在多于 1 个的情况下抛出。【参考方案4】:

它不仅仅是将连接设置为空。这可能是文档所说的,但这不是 mysql 的真相。连接会保持更长时间(我听说过 60 年代,但从未测试过)

如果您想在此处查看完整说明,请参阅连接https://www.php.net/manual/en/pdo.connections.php#114822 上的此评论

要强制关闭连接,您必须执行类似的操作

$this->connection = new PDO();
$this->connection->query('KILL CONNECTION_ID()');
$this->connection = null;

【讨论】:

感谢您的回答。这个问题是很久以前提出的,但是您对连接的权利。 我实际上并不同意通过 PHP 处理 TCP 连接是个好主意。所有底层 TCP 连接处理都被抽象掉了,所以我们只需要在运行时处理高层类和对象。 PHP 是一种基于请求的语言(您可能知道),因此终止与 dB 的潜在持久连接可能会导致用户出现意外错误/问题。您链接到的用例可能会导致驱动程序保持持久连接打开以供另一个请求使用,所以我认为这是预期的行为。 如果你真的查看mysql中的进程列表,它会显示连接仍然​​存在。我同意你不应该像这样搞乱 TCP 连接,并且应该有一种方法可以正确断开连接。但事实并非如此。所以,如果你真的想断开与服务器的连接,你将不得不做这样的事情。将连接设置为 null 不会断开与文档所说的相反的连接。 我找到了这个解释:***.com/a/18277327/1315873 PHP 8.0 SQLite 3.32.1 no such function: CONNECTION_ID【参考方案5】:
<?php if(!class_exists('PDO2')) 
    class PDO2 
        private static $_instance;
        public static function getInstance() 
            if (!isset(self::$_instance)) 
                try 
                    self::$_instance = new PDO(
                        'mysql:host=***;dbname=***',
                        '***',
                        '***',
                        array(
                            PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4 COLLATE utf8mb4_general_ci",
                            PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION
                        )
                    );
                 catch (PDOException $e) 
                    throw new PDOException($e->getMessage(), (int) $e->getCode());
                
            
            return self::$_instance;
        
        public static function closeInstance() 
            return self::$_instance = null;
        
    

$req = PDO2::getInstance()->prepare('SELECT * FROM table');
$req->execute();
$count = $req->rowCount();
$results = $req->fetchAll(PDO::FETCH_ASSOC);
$req->closeCursor();
// Do other requests maybe
// And close connection
PDO2::closeInstance();
// print output

完整示例,带有自定义类 PDO2。

【讨论】:

请从您的代码中删除 try catch 或添加一个新的 throw,如 here 所示。现在您的代码通常会滥用异常和错误报告

以上是关于PDO 关闭连接的主要内容,如果未能解决你的问题,请参考以下文章

PHP中PDO关闭连接的问题

在 PHP 中使用 PDO 打开的 SQL 连接是不是必须关闭

PDO::beginTransaction

PDO - 真实的事实和最佳实践? [关闭]

lastInsertId() 在插入操作中不适用于 PDO 对象[关闭]

PDO准备好的陈述[关闭]