【问题标题】:Too many connections with PDO persistent connection?与 PDO 持久连接的连接太多?
【发布时间】:2018-10-19 00:40:39
【问题描述】:

我已经为此苦苦挣扎了很长一段时间,到了需要寻求帮助的地步,因为即使进行了所有研究,我也无法弄清楚为什么会发生这种情况。

致命错误:未捕获异常 'PDOException' 并带有消息 'SQLSTATE[HY000] [1040] Too many connections'

这发生在加载单个页面 (index.php) 时,我是唯一的用户 (dev)。正如您在此处看到的,MySQL 连接限制设置为 @ 50,但我差一点超过了这个限制。这是对我重构代码之前创建的 100~ 个连接的改进。

这是页面加载一次后的统计数据。

我已将问题缩小到几个原因:

  • 我不完全了解 PDO/MySQL 连接的工作原理。
  • 我在我的代码中创建了太多的连接,即使我尝试只创建一个我可以共享的连接。
  • 我需要增加连接限制(似乎不太可能)。

我发现的大多数 SO 问题,告诉 OP 增加连接限制,而不知道这是否是最好的解决方案,所以如果不需要,我会尽量避免这种情况。一页加载 50 个连接似乎太多了。

这些是我在相关页面上实例化的类。

$DataAccess = new \App\Utility\DataAccess();
$DataCopyController = new App\Controllers\DataCopyController($DataAccess);
$DriveController = new App\Controllers\DriveController($DataAccess);
$Helper = new App\Utility\Helper();
$View = new App\Views\View();

我正在创建 DAL 对象,然后将其注入到需要它的类中。通过这样做,我希望只创建一个对象和一个连接,但这显然不是正在发生的事情。在 DAL 类中,我还在每个查询方法中添加了$this->DbConnect->close()

这是DataAccess() 类的构造函数。

public function __construct() {

    $this->DbConnect = new \App\Services\DbConnect();
    $this->db = $this->DbConnect->connect("read");
    $this->dbmod = $this->DbConnect->connect("write");

    $this->Helper = new Helper();
}

这是DbConnect() 类。

类 DbConnect {

  private $db;
  private $dbmod;

  private function isConnected($connection) {
    return ($connection) ? TRUE : FALSE;
  } 

  public function connect($access) {

    $options = [
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
                PDO::ATTR_EMULATE_PREPARES => false
                ];

    if ($access == "read") {
        if ($this->isConnected($this->db)) {
            return $this->db;
        } else {
            if (strpos($_SERVER['SERVER_NAME'], DBNAME_DEV) === false) {
                $this->db = new PDO("mysql:host=127.0.0.1; dbname=".DBNAME, 
                                                                    DBUSER, 
                                                                    DBPASS, 
                                                                    $options
                                                                       );
            } else {
                $this->db = new PDO("mysql:host=" . DBHOST_DEV ."; dbname=".DBNAME_DEV, 
                                                                   DBUSER, 
                                                                   DBPASS, 
                                                                   $options
                                                                       );
            }
            return $this->db;
        }
    } elseif ($access == "write") {
        if ($this->isConnected($this->dbmod)) {
            return $this->dbmod;
        } else {
            if (strpos($_SERVER['SERVER_NAME'], DBNAME_DEV) === false) {
                $this->dbmod = new PDO("mysql:host=127.0.0.1; dbname=".DBNAME, 
                                                                       DBUSER_MOD, 
                                                                       DBPASS, 
                                                                       $options
                                                                       );
            } else {
                $this->dbmod = new PDO("mysql:host=" . DBHOST_DEV . "; dbname=".DBNAME_DEV, 
                                                                       DBUSER_MOD, 
                                                                       DBPASS, 
                                                                       $options
                                                                       );
            }
        }
        return $this->dbmod;
    }
  }

  public function close() {
    $this->db = null;
    $this->dbmod = null;
  }
}

我也尝试在index.php 上实例化DbConnect() 类并注入它而不是DataAccess(),但结果是一样的。

编辑: 我还想补充一点,这个 MySQL 服务器有两个数据库,prod 和 dev。我想连接限制在两者之间共享。但是,prod 数据库的流量很少,我在那里没有看到这个错误。当我刷新统计信息时,没有与 prod 数据库的连接。

【问题讨论】:

  • 据我了解,您不会想要关闭持久连接。你在哪里打电话给DbConnect#close()
  • 另外,来自the manual ~ "如果你想使用持久连接,你必须在传递给PDO构造函数的驱动选项数组中设置PDO::ATTR_PERSISTENT。如果设置这个属性在对象实例化后使用 PDO::setAttribute(),驱动程序将不会使用持久连接。"
  • @Phil 谢谢。所以我的持久连接不是持久的。我也会尽量不关闭连接,让你知道结果。
  • @Phil 成功了!但似乎还是有什么奇怪的地方。在 mysql 服务器上,我看到 20 个持久连接。 db 实例为 10,dbmod 实例为 10。
  • 我关闭了持久连接,但在页面加载时仍然只看到 2 个连接。似乎关闭连接可能是原因。奇怪,但这已经解决了。 @Phil,如果您添加答案,我会接受。

标签: php mysql pdo


【解决方案1】:

来自 PHP 手册 ~http://php.net/manual/en/pdo.connections.php

许多 Web 应用程序都将从与数据库服务器建立持久连接中受益。持久连接不会在脚本结束时关闭,但会在另一个脚本使用相同凭据请求连接时被缓存并重新使用。

所以我建议删除 DbConnection#close() 方法,因为你不想调用它。

也来自手册...

注意:
如果您希望使用持久连接,则必须在传递给 PDO 构造函数的驱动程序选项数组中设置 PDO::ATTR_PERSISTENT。如果在对象实例化后将该属性设置为PDO::setAttribute(),驱动程序将不会使用持久连接。

所以你会想要(至少)

new \PDO("mysql:host=127.0.0.1;dbname=" . DBNAME, DBUSER, DBPASS, [
    PDO::ATTR_PERSISTENT => true
]);

您还可以在构造函数中设置其他连接属性。

【讨论】:

  • 我最终还是无法使用持久连接。正如我提到的那样,有很多,因为它们似乎正在超时并创建一个新的。感谢您的帮助。
  • 我怀疑它们都是存在于持久连接缓存中的连接。尝试先清除所有内容,然后查看它们是否继续堆叠
  • @Nick 我似乎找不到任何相关信息。如果 PHP 作为 Apache 模块运行,我想缓存与 Apache 进程相关联。重新启动/重新加载网络服务器应该清除它
  • @Phil 不用担心,感谢您的关注。我已经看到了类似的问题,并且想知道该缓存是否可能是我看到的数字的原因。重新启动服务器对我来说并不是一个真正的选择,我想通过持久连接来坚持:),因为它们给了我速度优势。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-24
  • 2015-04-27
  • 2010-12-01
  • 2011-07-05
  • 2020-01-12
相关资源
最近更新 更多