2016-08-23 36 views
2

我有一個Heroku Dyno託管的網站,允許最大512MB的內存。Heroku Dyno上整體內存上升

我的網站允許用戶以CSV格式上傳原始時間序列數據,並且我想要加載測試上傳具有〜100k行(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%) 

我可以重新啓動測功機來清除這個問題,但我沒有在看指標太多的經驗,所以我想明白了什麼正在發生。

  • 如果我的工作在30分鐘內完成,內存使用量可能會持續增長的常見原因是什麼?在工作之前,它很穩定
  • 有沒有辦法告訴哪些數據被存儲在內存中?儘管我不知道它是否會比十六進制地址數據更多,但是執行內存轉儲會很棒
  • 我可以使用哪些其他工具來更好地瞭解情況?我可以通過上傳另一個大文件以收集更多數據來重現這種情況。

只是有點迷路了,在哪裏開始調查。

謝謝!

編輯: - 我們有收集數據的Heroku New Relic插件。令人懊惱的是,New Relic在同一時間段報告了不同的/正常的內存使用情況值。這是常見的嗎?它是什麼測量?

enter image description here

回答

1

有針對最可能的原因:

方案1。你先處理整個文件,首先將CSV中的每條記錄加載到內存中,然後進行一些處理,然後迭代並存儲到數據庫中。

如果是這種情況,那麼你需要改變你的實現來分批處理這個文件。加載100條記錄,處理它們,存儲在數據庫中,重複。你也可以看看activerecord-import寶石來加速你的插入。

情景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。它維護已知內存泄漏問題的寶石列表。如果您從此列表中獲得了某些信息,我建議您儘快更新它。

現在解決您的其他問題。如果您在Heroku或任何其他提供商上擁有完美健康的應用程序,則隨着時間的推移,您將始終看到內存在增加,但它應該在某個時間保持穩定。 Heroku每天都會重新啓動一次dynos。根據您的指標,您會看到突然下降以及跨越2天左右的緩慢增長。

默認情況下New Relic顯示所有實例的平均數據。您可能應該切換到只顯示您的工人dyno的數據,以查看正確的內存使用情況。

最後,我建議閱讀this article關於Ruby如何使用內存。這裏提到了很多有用的工具,特別是derailed_benchmarks。它是由Heroku(當時)的人創建的,它是與人們在Heroku上遇到的最常見問題相關的許多基準的集合。

+0

謝謝,這一切都非常有用!我不知道你可以像這樣觀察對象空間,所以我會嘗試在幾個點上打印出對象的數量,看看我是否注意到了任何東西。我也會看看那篇文章。 – user2490003