Heroku Dyno上的总内存增加

时间:2016-08-23 05:01:42

标签: ruby-on-rails memory heroku newrelic

我有一个在Heroku Dyno上托管的网站,允许最大512MB的内存。

我的网站允许用户以CSV格式上传原始时间序列数据,我想加载测试上传一行~100万行(3.2 MB大小)的CSV的性能。用户界面允许用户上传文件,该文件依次启动Sidekiq作业,将文件中的每一行导入我的数据库。它将上传的文件存储在dyno上的/tmp存储下,我相信每次定期重启dyno时都会清除它。

实际完成的所有内容都没有错误,并且插入了所有100k行。但几个小时后,我注意到我的网站几乎没有响应,我检查了Heroku指标。

在我开始上传的确切时间,内存使用率开始增长,并迅速超过最大512MB。

enter image description here

日志证实了这一事实 -

# At the start of the job
Aug 22 14:45:51 gb-staging heroku/web.1:  source=web.1 dyno=heroku.31750439.f813c7e7-0328-48f8-89d5-db79783b3024 sample#memory_total=412.68MB sample#memory_rss=398.33MB sample#memory_cache=14.36MB sample#memory_swap=0.00MB sample#memory_pgpgin=317194pages sample#memory_pgpgout=211547pages sample#memory_quota=512.00MB 

# ~1 hour later
Aug 22 15:53:24 gb-staging heroku/web.1:  source=web.1 dyno=heroku.31750439.f813c7e7-0328-48f8-89d5-db79783b3024 sample#memory_total=624.80MB sample#memory_rss=493.34MB sample#memory_cache=0.00MB sample#memory_swap=131.45MB sample#memory_pgpgin=441565pages sample#memory_pgpgout=315269pages sample#memory_quota=512.00MB 
Aug 22 15:53:24 gb-staging heroku/web.1:  Process running mem=624M(122.0%) 

我可以重启Dyno来解决这个问题,但是我在查看指标方面没有多少经验,所以我想了解发生了什么。

  • 如果我的工作在约30分钟内完成,那么内存使用量可能会持续增长的常见原因是什么?在工作之前它非常稳定
  • 有没有办法告诉哪些数据存储在内存中?做内存转储会很棒,虽然我不知道它是否会超过十六进制地址数据
  • 我可以使用哪些其他工具来更好地了解情况?我可以通过上传另一个大文件来收集更多数据来重现这种情况

从哪里开始调查只是有点迷失。

谢谢!

编辑: - 我们有Heroku New Relic插件,它也可以收集数据。令人讨厌的是,New Relic报告了同一时间段内不同/正常的内存使用价值。这是常见的吗?它测量的是什么?

enter image description here

1 个答案:

答案 0 :(得分:2)

最可能的原因是:

场景1 。您处理整个文件,首先将每条记录从CSV加载到内存,进行一些处理,然后迭代并存储到数据库中。

如果是这种情况,那么您需要更改实施以批量处理此文件。加载100条记录,处理它们,存储在数据库中,重复。您还可以查看activerecord-import gem以加快插入速度。

场景2 。脚本中有内存泄漏。也许您是批量处理,但是您持有对未使用对象的引用,并且它们不是垃圾回收。

您可以使用ObjectSpace模块找到答案。它有一些非常有用的方法。

count_objects将返回散列,其中包含当前在堆上创建的不同对象的计数:

ObjectSpace.count_objects
=> {:TOTAL=>30162, :FREE=>11991, :T_OBJECT=>223, :T_CLASS=>884, :T_MODULE=>30, :T_FLOAT=>4, :T_STRING=>12747, :T_REGEXP=>165, :T_ARRAY=>1675, :T_HASH=>221, :T_STRUCT=>2, :T_BIGNUM=>2, :T_FILE=>5, :T_DATA=>1232, :T_MATCH=>105, :T_COMPLEX=>1, :T_NODE=>838, :T_ICLASS=>37}

它只是一个哈希,所以你可以寻找特定类型的对象:

ObjectSpace.count_objects[:T_STRING]
=> 13089

您可以将此代码段插入脚本中的不同位置,以查看特定时间堆上有多少对象。要获得一致的结果,您应该在检查计数之前手动触发垃圾收集器。它将确保您只能看到活动对象。

GC.start
ObjectSpace.count_objects[:T_STRING]

另一个有用的方法是each_object,它迭代实际堆上的所有对象:

ObjectSpace.each_object { |o| puts o.inspect }

或者你可以迭代一个类的对象:

ObjectSpace.each_object(String) { |o| puts o.inspect }

场景3 。宝石或系统库中存在内存泄漏。

这与之前的方案类似,但问题不在于您的代码。您也可以使用ObjectSpace找到此信息。如果在调用库方法后看到有一些对象被保留,则该库可能会有内存泄漏。解决方案是更新这样的库。

看看这个repo。它维护具有已知内存泄漏问题的gem列表。如果您有此列表中的内容,我建议您快速更新。

现在解决您的其他问题。如果您在Heroku或任何其他提供商上拥有完全健康的应用程序,您将始终看到内存随着时间的推移而增加,但它应该在某个时候稳定下来。 Heroku每天重启一次dynos。根据您的指标,您将看到突然下降和2天左右的缓慢增长。

默认情况下,New Relic显示所有实例的平均数据。您应该切换到仅显示来自工作人员dyno的数据,以查看正确的内存使用情况。

最后,我建议阅读有关Ruby如何使用内存的this article。这里提到了许多有用的工具,特别是derailed_benchmarks。它是由Heroku的人(当时)创建的,它是许多与人们在Heroku上遇到的最常见问题相关的基准测试的集合。