【问题标题】:Memory leaks on postgresql server after upgrade to Rails 4升级到 Rails 4 后 postgresql 服务器上的内存泄漏
【发布时间】:2014-03-26 10:03:54
【问题描述】:

我们在 Heroku 上的 Rails 应用程序中遇到了一个奇怪的问题。从 Rails 3.2.17 迁移到 Rails 4.0.3 后,我们的 postgresql 服务器显示内存使用量无限增加,然后它在每个请求上都返回以下错误:

ERROR: out of memory
DETAIL: Failed on request of size xxx

刚刚发布带有rails 4的应用程序后,postgresql内存开始增加。

正如您在下面的屏幕截图中看到的,它在 3 小时内从 500 MO 增加到超过 3,5Go

同时,每秒提交翻倍。它每秒提交 120 次:

每秒提交 280 次:

值得注意的是,当我们重新启动应用程序时,内存下降到正常值 600 Mo,然后在几个小时后上升到 3 Go 以上(然后每个 sql 请求都会显示“内存不足”错误)。这就像杀死 ActiveRecord 连接正在释放 postgresql 服务器上的内存。

我们很可能在某个地方发生了内存泄漏。 但是:

  • 它在 Rails 3.2 中运行良好。也许这个问题是我们为使代码适应 Rails 4 所做的更改与 Rails 4 代码本身的结合。
  • Rails 4 升级后每秒提交次数的增加似乎很奇怪。

我们的堆栈是:

  • Heroku,x2 dynos
  • Postgresql、Ika 计划在 heroku 上
  • 独角兽,每个实例 3 个工人
  • Rails 4.0.3
  • Redis 缓存。
  • 值得注意的宝石:延迟工作 (4.0.0)、Active Admin(在主分支上)、舒适的墨西哥沙发 (1.11.2)

在我们的代码中似乎没有什么特别的。

我们的 postgresql 配置是:

  • work_mem:100MB
  • shared_buffers:1464MB
  • 最大连接数:500
  • maintenance_work_mem:64MB

有人在切换到 Rails 4 时遇到过这种行为吗?我也在寻找重现的想法。

非常欢迎所有帮助。

提前致谢。

【问题讨论】:

  • Pg 中的 shared_buffersmax_connectionswork_memmaintenance_work_mem 是什么?
  • 感谢 Craig,问题已根据这些信息更新。
  • 您的最大 RAM 使用量至少为 100MB * 500 + 1464MB,因此接近 52GB。难怪你有问题。特别是因为单个会话可以使用几次work_mem 进行更复杂的查询(它是按操作而不是按查询)。
  • 感谢 Craig 让我注意到这一点。这些设置是 heroku postgresql 默认设置,我会调查以了解如何优化它。但是,我们最多只使用 40 个连接。如果我算一下,它会给出 5.5GB。这在我们在 heroku 上的 postgresql 计划的内存之下。
  • 克雷格,我重新更新了问题,删除了标题上的前缀 Heroku。我认为 Heroku 在这里只是一个工具,我觉得问题可能与 Heroku PAAS 无关。

标签: ruby-on-rails postgresql heroku


【解决方案1】:

我不知道什么更好:回答我的问题或更新它......所以我选择回答。如果更新更好,请告诉我

我们终于找到了问题所在。从 3.1 版开始,Rails 为 User.find(id) 等简单请求添加了预处理语句。 4.0 版,在关联请求中添加了准备好的语句(has_many、belongs_to、has_one)。 例如以下代码:

class User
  has_many :adresses
end
user.addresses

生成请求

SELECT "addresses".* FROM "addresses" WHERE "addresses"."user_id" = $1  [["user_id", 1]]

问题在于 Rails 只为外键(这里是 user_id)添加准备好的语句变量。如果您使用自定义 sql 请求,例如

user.addresses.where("moved_at < ?", Time.now - 3.month) 

它不会将变量添加到为moved_at 准备好的语句中。所以每次调用请求时它都会生成一个准备好的语句。 Rails 处理最大大小为 1000 的池的预处理语句。

但是,postgresql 的prepared statements 不是跨连接共享的,因此每个连接在一两个小时内就有1000 个prepared statements。其中一些非常大。这导致 postgreqsl 服务器上的内存消耗非常高。

【讨论】:

  • 谢谢。谢谢你。谢谢!这已经困扰了我整整一个月,直到我发现这个。
  • 回答你自己的问题是可以的。您也应该接受它作为正确答案。
  • 非常感谢!你拯救了我的一天!
  • statement_limit 删除到我们的database.yml 文件中的200 为我们解决了这个问题!
猜你喜欢
  • 2020-01-15
  • 2014-06-16
  • 2011-11-12
  • 2012-04-23
  • 2012-08-23
  • 1970-01-01
  • 2011-07-21
  • 2013-08-27
  • 2012-03-18
相关资源
最近更新 更多