You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

扩展Phalcon Mysql PDO适配器时遭遇Too many connections错误求助

解决Phalcon数据库适配器扩展中的"Too many connections"错误

你遇到的问题根源是循环依赖导致的重复创建数据库连接,虽然错误栈显示在parent::__construct这一行,但实际的调用链暴露了更深层的问题:

问题分析

从错误栈的调用逻辑可以看出:

  1. 你的Internal\Database\Mysql构造函数先调用父类构造,尝试创建数据库连接
  2. 紧接着你调用Di::getDefault()->get('logger')获取日志服务
  3. 而logger服务的解析又依赖了logs服务(栈中第6行)
  4. 关键的是,logs服务的初始化过程中又尝试获取了数据库服务(也就是你的Mysql类)

这就形成了一个无限循环:
创建Mysql实例 → 获取logger → 获取logs → 创建Mysql实例 → ...

每一次循环都会新建一个数据库连接,直到超出MySQL的max_connections限制,最终触发SQLSTATE[08004] [1040] Too many connections错误。

解决方案

针对这个循环依赖问题,有几种可行的修复方式:

1. 延迟获取Logger(最简单的修复)

不在构造函数中主动初始化Logger,而是在真正需要使用它的时候(也就是query失败时)再获取:

修改你的Mysql类代码:

namespace Internal\Database;
use Phalcon\Db\Adapter\Pdo\Mysql as PhalconMysql;
use Phalcon\Di;

class Mysql extends PhalconMysql {
    public $isLogger = false;
    private $oLogger; // 改为私有属性更规范

    public function __construct(array $descriptor) {
        parent::__construct($descriptor);
        // 移除构造函数中获取Logger的代码
    }

    public function query($sqlStatement, $bindParams = null, $bindTypes = null) {
        $oResult = parent::query($sqlStatement, $bindParams, $bindTypes);
        if ($oResult === false && $this->isLogger === false) {
            // 延迟初始化Logger,仅在需要时获取
            if (!isset($this->oLogger)) {
                $this->oLogger = Di::getDefault()->get('logger');
            }
            $trace = debug_backtrace();
            $aCaller = array_shift($trace);
            $sFile = $aCaller['file'];
            $sLine = $aCaller['line'];
            $this->oLogger->error('MySQL query failed. File: ' . $sFile . ', Line: ' . $sLine, ['error' => $this->getErrorInfo()]);
        }
        return $oResult;
    }
}

这种方式避免了构造阶段的循环依赖,只有当查询失败时才会触发Logger的获取,此时数据库连接已经创建完成,不会再重复初始化数据库服务。

2. 调整服务初始化顺序

如果你的logs服务本身依赖数据库(比如日志写入数据库),那需要调整DI中服务的注册顺序:

  • 先注册logger和logs服务(确保它们的初始化不依赖未创建的数据库服务)
  • 再注册db服务

同时,确保logs服务使用惰性加载:不要在服务注册时就创建数据库连接,而是在实际写入日志时才获取连接。

3. 被动注入Logger(更符合依赖注入原则)

在注册数据库服务时,主动将Logger注入到Mysql实例中,而不是让Mysql主动从DI中获取:

修改DI的服务注册代码(比如core_services.php):

$di->set('db', function() use ($di) {
    // 加载数据库配置
    $dbConfig = $di->get('config')->database;
    // 创建Mysql实例
    $db = new \Internal\Database\Mysql($dbConfig->toArray());
    // 主动注入Logger(确保Logger已经注册)
    $db->oLogger = $di->get('logger');
    return $db;
});

然后修改Mysql类的构造函数,移除主动获取Logger的代码即可。

额外验证

建议检查你的logs服务定义,确认它是否真的需要依赖数据库服务。如果日志只是写入文件,那完全可以移除它对数据库的依赖,从根源上避免循环问题。

内容的提问来源于stack exchange,提问作者Kate Greenfield

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:44:19