使用Ruby on Rails时出现“MySQL服务器已关闭”错误

42

我们的Ruby on Rails应用程序运行一段时间后,会突然出现500错误,并显示“MySQL server has gone away”(MySQL服务器已断开连接)的错误信息。通常情况下这种情况是在夜间发生的。但是我们的服务器配置没有明显的更改。

 Mysql::Error: MySQL server has gone away: SELECT * FROM `widgets`

重新启动mongrels(而非MySQL服务器)会解决此问题。

我们该如何修复这个问题?

12个回答

62

22

这可能是由于与MySQL的持久连接断开导致的(如果在夜间发生,则很可能是超时),而Ruby on Rails未能默认恢复连接:

在文件vendor/rails/actionpack/lib/action_controller/dispatcher.rb中,代码如下:

if defined?(ActiveRecord)
  before_dispatch { ActiveRecord::Base.verify_active_connections! }
  to_prepare(:activerecord_instantiate_observers) {ActiveRecord::Base.instantiate_observers }
end

verify_active_connections! 方法执行多个操作之一,其中之一是重新创建任何过期的连接。

这种错误最可能的原因是因为 monkey patch 重新定义了调度程序以不调用 verify_active_connections!,或者 verify_active_connections! 已经被更改了等。


2
是的,我通过一次捕获 ActiveRecord::StatementInvalid 并在遇到此问题时手动调用 ActiveRecord::Base.verify_active_connections! 来解决了这个问题。 - conny
@Conny,ActiveRecord::StatementInvalid应该放在哪里?是在application_controller.rb吗?还是其他地方? - Christian Fazzini
1
@ChristianFazzini,这是在一个独立的守护进程中,它提供了一个特定于应用程序的目的:我从来没有在一个普通的Rails Web应用程序中遇到过这个问题。如果我有的话,现在我会使用Rails 2.3或更新版本,并使用重新连接选项如mixonic所建议的 - conny

9

在Ruby on Rails 4中,尝试使用ActiveRecord::Base.connection.verify!。Verify会向服务器发送一个测试请求,如果连接断开,则重新连接。


5

当向MySQL发送非常大的语句时,我遇到了这个问题。MySQL会限制语句的大小,并且如果超过限制,它将关闭连接。

set global max_allowed_packet = 1048576; # 2^20 bytes (1 MB) was enough in my case

那正是我所面临的问题,谢谢您先生。 - amrdruid

4

正如这个帖子的其他贡献者所说,最有可能的是MySQL服务器由于闲置而关闭了与Ruby on Rails应用程序的连接。默认超时时间为28800秒,即8小时。

set-variable = wait_timeout=86400

将这一行添加到您的/etc/my.cnf中,将会将超时时间提高到24小时。http://dev.mysql.com/doc/refman/5.0/en/server-system-variables.html#option_mysqld_wait_timeout。虽然文档没有说明,但是值为0可能会完全禁用超时,但需要进行实验以确定。
我知道还有三种情况会导致出现此错误。第一种情况是MySQL服务器被重启。这显然会断开所有连接,但由于MySQL客户端是被动的,直到下一次查询才会注意到这一点。
第二种情况是如果有人从MySQL命令行中杀死了您的查询,这也会断开连接,因为它可能会使客户端处于未定义状态。
最后一种情况是,如果您的MySQL服务器由于致命的内部错误而重新启动。也就是说,如果您对一个表进行简单的查询,立即看到“MySQL已经离开”,那么我建议您仔细查看服务器日志,检查硬件错误或数据库损坏。

在我所工作的Rails应用中,我没有看到Active Record在当前请求期间重新连接,但它会在下一个请求时重新连接,因此如果您可以每个Mongrel复制一次异常,那么这可能是一个临时条件。 - Dave Cheney

2
首先,在MySQL中确定max_connections:
show variables like "max_connections";

你需要确保在 Ruby on Rails 应用程序中建立的连接数少于允许的最大连接数。请注意,额外的连接可能来自于你的 cron 作业、delayed_job 进程(每个进程在你的 database.yml 中都有相同的池大小)等。
通过在 MySQL 中执行以下操作,在浏览应用程序、运行进程等过程中监视 SQL 连接:
show status where variable_name = 'Threads_connected';

你可能想考虑在线程执行完成后关闭连接,因为数据库连接不会自动关闭(我认为这在Ruby on Rails 4应用程序中不是太大的问题Reaper):
Thread.new do
  begin
     # Thread work here
  ensure
     begin
        if (ActiveRecord::Base.connection && ActiveRecord::Base.connection.active?)
           ActiveRecord::Base.connection.close
        end
      rescue
      end
  end
end

1
在 database.yml 文件中使用 reconnect: true 将导致数据库连接在 ActiveRecord :: StatementInvalid 错误被引发之后重新建立(正如 Dave Cheney 所提到的)。
不幸的是,为了防止连接超时,添加对数据库操作的重试似乎是必要的:
begin
  do_some_active_record_operation
rescue ActiveRecord::StatementInvalid => e
  Rails.logger.debug("Got statement invalid #{e.message} ... trying again")
  # Second attempt, now that db connection is re-established
  do_some_active_record_operation
end

1

连接到MySQL服务器可能超时了。

您应该能够在MySQL中增加超时时间,但为了得到一个正确的解决方案,您的代码应该检查数据库连接是否仍然存在,如果不存在则重新连接。


1
你是否监控MySQL的连接数或线程数?你的mysql.ini文件中的max_connections设置是多少?
mysql> show status;

查看Connections、Max_used_connections、Threads_connected和Threads_created。

您可能需要在MySQL配置中增加限制,或者Rails没有正确关闭连接*。

注意:我只是简单地使用过Ruby on Rails...

有关服务器状态的MySQL文档在http://dev.mysql.com/doc/refman/5.0/en/server-status-variables.html中。


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接