MySQL”;消失了”;持久PHP连接出错


MySQL "Gone Away" Error with Persistent PHP Connection

我在WAMP堆栈上本地托管一个网站。最近,我通过在PDO构造函数选项参数中添加array(PDO::ATTR_PERSISTENT => true),将PHP连接切换为持久连接。我注意到响应时间因此大幅下降(万岁!)。

缺点似乎是当机器醒来时出现了一个消失的错误。在更改连接样式之前从未发生过这种情况。

缓存的连接是否可能已关闭,但仍在继续返回?是否可以通过catch块内的PHP重置PDO连接或连接池?

我已经讨论了几天了,基于网络上类似问题的普遍性,这似乎是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不足的地方。无法强制打开连接,甚至无法检测状态。

我目前解决这个问题的方法(诚然有点黑客)是发布以下MySQL语句

set @@GLOBAL.interactive_timeout := 86400;
set @@GLOBAL.wait_timeout := 86400;

默认情况下,这些变量设置为28800秒(8小时)。请注意,您需要重新启动Apache以清除缓存的连接,否则在池中的所有连接都被循环之前,您不会注意到差异(我不知道这种情况是如何发生的/何时发生的)。我选择了86400,也就是24小时,我每天都在这台机器上,所以这应该可以满足基本需求。

在这次更新之后,我让我的机器至少坐了12个小时,这是我之前收到"离开消息"时它坐的时间。看起来问题已经解决了。

我一直在想,虽然我不能强制打开连接,但可以从池中删除坏连接。我还没有尝试过,但一个稍微更优雅的解决方案可能是检测"消失"消息,然后将对象设置为NULL,告诉PHP销毁资源。如果数据库逻辑进行了几次这样的尝试(必须有一个限制,以防发生更严重的错误),这可能有助于将这些错误降至最低。

值得一提的是,我正在研究在nginx后面的php fpm 7.3上使用持久连接,并试图用1个孩子的静态池来重现这种行为,但到目前为止我还做不到。

我可以在一个单独的终端上通过SHOW PROCESSLIST看到,数据库是如何在执行SELECT的请求5秒钟后关闭持久连接的,但下一个请求只是打开一个新的连接,并且运行良好。另一方面,如果我用负载测试工具轰炸API,则会保持相同的连接,并且所有请求都会成功。

也许是因为您使用了Apache+mod_php而不是php-fpm工作池,或者可能在PHP5.4和7.3之间进行了真正的修复

测试版本:

PHP-FPM: 7.3.13
mysqlnd (underlying PDO_MYSQL driver): 5.0.12-dev - 20150407
MySQL Server: 5.7.29 and 8.0.19
MariaDB Server (MySQL drop-in replacement): 10.1.43

PD感谢您列出复制步骤和思考过程,这是非常宝贵的。

是的,如果连接关闭,则需要重新连接。

http://brady.lucidgene.com/2013/04/handling-pdo-lost-mysql-connection-error/