PHP activerecord mysql 服务器已经消失

PHP activerecord mysql server has gone away

我正在使用 php-activerecord for a short while now and i absolutely love it. Php-activerecord is an open source ORM library based on the ActiveRecord pattern. However, i recently tried to use it in combination with a websocket application based on Wrench

这非常有效,但要启动脚本,应用程序必须 运行 作为 linux 上的守护程序,以便使 websockets 始终可用。在一段时间不使用该应用程序然后再次尝试使用它后,它会抛出一些数据库异常:

一开始它给出警告:

PHP Warning: Error while sending QUERY packet. PID=XXXXX in /home/user/domains/example.com/public_html/vendor/php-activerecord/php-activerecord/lib/Connection.php on line 322

然后抛出致命错误:

PHP Fatal error: Uncaught exception 'ActiveRecord\DatabaseException' with message 'exception 'PDOException' with message 'SQLSTATE[HY000]: General error: 2006 MySQL server has gone away' in /home/user/domains/example.com/public_html/vendor/php-activerecord/php-activerecord/lib/Connection.php:322

堆栈跟踪:

#0 /home/user/domains/example.com/public_html/vendor/php-activerecord/php-activerecord/lib/Connection.php(322): PDOStatement->execute(Array)

#1 /home/user/domains/example.com/public_html/vendor/php-activerecord/php-activerecord/lib/Table.php(218): ActiveRecord\Connection->query('SELECT * FROM ...', Array)

#2 /home/user/domains/example.com/public_html/vendor/php-activerecord/php-activerecord/lib/Table.php(209): ActiveRecord\Table->find_by_sql('SELECT * FROM `...', Array, false, NULL)

#3 /home/user/domains/example.com/public_html/vendor/php-activerecord/php-activerecord/lib/Model.php(1567): ActiveRecord\Table->find(Array)

#4 in /home/user/domains/example.com/public_html/vendor/php-activerecord/lib/Connection.php on line 325

似乎 php-activerecord 一直保持 mysql 连接打开,而 websocket 服务器 运行ning,这当然应该不是问题自动尝试重新连接并再次 运行 查询。但事实并非如此。

我读过一些关于设置 MYSQL_OPT_RECONNECT 的内容。但我不确定这是否有效或如何使用 php-activerecord 设置该选项。这里有人有这方面的经验吗?

编辑:这是我的全局超时配置变量

VARIABLE_NAME                   VARIABLE_VALUE  
DELAYED_INSERT_TIMEOUT          300
WAIT_TIMEOUT                    28800
CONNECT_TIMEOUT                 10
LOCK_WAIT_TIMEOUT               31536000
INNODB_ROLLBACK_ON_TIMEOUT      OFF
THREAD_POOL_IDLE_TIMEOUT        60
NET_WRITE_TIMEOUT               60
INNODB_LOCK_WAIT_TIMEOUT        50
INTERACTIVE_TIMEOUT             28800
DEADLOCK_TIMEOUT_LONG           50000000
SLAVE_NET_TIMEOUT               3600
DEADLOCK_TIMEOUT_SHORT          10000
NET_READ_TIMEOUT                30

MySQL 服务器消失错误最常见的原因是服务器超时并关闭了连接。

尝试进行以下更改。

max_allowed_packet=64M

如果你有很多要求,设置这个,不要设置大,因为它与你的环境有关。

max_connections=1000

将此行添加到 my.cnf 文件中可能会解决您的问题。完成更改后重新启动 MySQL 服务。

阅读更多关于MySQL server has gone away

如果它不起作用,请尝试 this 自动重新连接功能。

如前所述,PHP 脚本中的 MySQL 当两者之间一段时间没有通信时超时。 这是一件好事,因为空闲连接会耗尽您的服务器资源。

"Server has gone away" 错误主要发生在两个查询之间发生相对冗长的计算时。

为了防止这种情况,您可以

  • 在执行期间定期执行 SELECT 1 查询
  • 围绕您的查询创建一个包装器,在执行之前检查连接是否有效
  • 使用来自this post
  • 的回答

但是,我认为重新配置 MySQL 以保持连接打开的时间更长会鼓励粗心的编程,因此建议不要这样做。

为了解决此类问题,我建议您:

  1. 使用 Gearman 作业服务器 (http://gearman.org/) 分发您的流程
  2. 使用 Supervisor 轻松管理这些进程 (http://supervisord.org/)

方法如下。

运行 您的 Web 套接字应用程序作为守护进程,就像您现在所做的一样(可能使用 cron)。或者更好的是,使用 Supervisor 管理它。配置它,以便 Supervisor 在 Supervisor 启动时启动它,并在它死亡时自动重新启动守护程序。

示例配置:

[program:my-daemon]
command=/usr/bin/php /path/to/your/daemon/script
autostart=true
autorestart=true

接下来,代替 运行 应用程序守护程序内部的查询处理,创建一个 Gearman Worker 来处理它。注册后,Worker 将等待 run/called。您必须从您的 websocket 应用程序调用 Worker,并在必要时提供必要的工作负载参数(有关此工作负载术语的解释,请参阅 Gearman 网站)。

在 Worker 中,当它已经完成守护程序请求的工作时,将其设置为 stop/exit。有了这个,你将不会有 "mysql server has gone away problem" 因为连接会立即关闭。

最后,我们必须让 Worker 像守护进程一样始终可用。所以,和daemon类似,配置Supervisor在Workerdies/stops时自动启动和自动重启,像这样:

[program:my-worker]
command=/usr/bin/php /path/to/your/worker/script
autostart=true
autorestart=true

另一件有趣的事情是,您可以添加任意数量的 Worker 以等待存活。只需添加以下配置:

numprocs=9 #change it to any number
process_name=%(program_name)s_%(process_num)02d #to identity worker number

因为我们告诉 Supervisor 自动重启每个进程,我们总是在后台有常量的 Workers 运行。

这里是关于这个策略的另一种解释:http://www.masnun.com/2011/11/02/gearman-php-and-supervisor-processing-background-jobs-with-sanity.html

希望对您有所帮助!

它也可能是查询的大小,因为有时 ORM 会合并查询以提高性能。

试试设置max_allowed_packet=128M,至少对诊断有用

PHP ActiveRecord 使用 PDO。关闭一个PDO连接是绝对没办法的,它是错误的DB层long 运行ning后台任务。

您可以尝试使用以下代码段影响 断开 PDO 连接。

//if not using ZF2 libraries, disconnect in some other way
$db->getDriver()->getConnection()->disconnect()
$db = NULL;
gc_collect_cycles();

断开连接,将您的引用设置为 null,然后 运行 垃圾收集器。希望这将调用 PDO 的内部 __destruct 方法来实际关闭连接。

必须在您自己的长 运行ning 脚本中管理您的数据库连接。如果您的工作人员有一段时间没有处理工作,您必须断开连接,并且当您有工作时必须重新连接。

真正的解决办法是不使用PDO,正常断线重连

如果您只是将服务器和客户端库超时设置为无限,您将 运行 陷入永不消亡的失控脚本问题,迫使您重新启动整个服务器(这不是个好主意弄乱超时)。

编辑:我实际上遇到了这个确切的问题并在去年的工作中使用了这个确切的解决方案。这解决了我 99% 的问题。但是,每隔一段时间就会出现一个杂散连接异常,我无法捕获并尝试重新连接。我只是每天重新启动一次进程,以摆脱那些杂散的连接错误。这就是为什么我的回答是,不要使用 PDO。立即切换并真正控制断开连接和重新连接。

如果您的数据库不处理多个并发连接和查询,您可以设置 "infinite" 超时。这不会显着影响数据库资源。最好的方法是发送 ping 数据包 (SELECT 1) 以更新超时并保持连接。