扩展Phalcon Mysql PDO适配器时遭遇Too many connections错误求助
你遇到的问题根源是循环依赖导致的重复创建数据库连接,虽然错误栈显示在parent::__construct这一行,但实际的调用链暴露了更深层的问题:
问题分析
从错误栈的调用逻辑可以看出:
- 你的
Internal\Database\Mysql构造函数先调用父类构造,尝试创建数据库连接 - 紧接着你调用
Di::getDefault()->get('logger')获取日志服务 - 而
logger服务的解析又依赖了logs服务(栈中第6行) - 关键的是,
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

