我已经解决了几天这个问题,根据网络上类似问题的普遍性,这似乎是 PDO 的缺陷,阻碍了持久连接的有效管理。
对显而易见的问题的回答:
- PHP 5.4.22
- php.ini 中的驱动程序设置已开启持久连接
- 会话限制不受限制(设置为 -1)
- 池限制不受限制(设置为 -1)
我可以通过执行以下操作重新创建问题:
在 MySQL 数据库上发出以下语句。
set @@GLOBAL.interactive_timeout := 10;
set @@GLOBAL.wait_timeout := 10;
向服务器发出一些请求以生成一些缓存连接。与使用非持久连接相比,您可以通过以下方式看到线程数增加:
echo $conn->getAttribute(PDO::ATTR_SERVER_INFO);
等待至少 10 秒,然后开始发出更多请求。您应该开始收到“离开”消息。
问题是 SQL 关闭连接,随后对 PDO 构造函数的调用返回这些关闭的连接重新连接它们。
这就是 PDO 的不足之处。没有办法强制打开连接,甚至没有检测状态的好方法。
我目前解决这个问题的方法(诚然有点 hack)是发出这些 MySQL 语句
set @@GLOBAL.interactive_timeout := 86400;
set @@GLOBAL.wait_timeout := 86400;
These variables are set to 28800sec (8 hours) by default。请注意,您需要重新启动 Apache 以清除缓存的连接,否则在池中的所有连接都已循环之前您不会注意到差异(我不知道这种情况如何/何时发生)。我选择了 86400,即 24 小时,我每天都在这台机器上,所以这应该可以满足基本需求。
在此更新之后,我让我的机器至少放置了 12 个小时,这是我之前开始收到“离开消息”时的放置时间。看起来问题解决了。
我一直在想,虽然我不能强制打开连接,但有可能从池中删除坏连接。我还没有尝试过,但稍微更优雅的解决方案可能是检测“消失”消息,然后将对象设置为 NULL,告诉 PHP 销毁资源。如果数据库逻辑进行了几次这样的尝试(必须有一个限制,以防发生更严重的错误),它可能有助于将这些错误保持在最低限度。