2010-08-18 76 views
8

我打算使用延遲作業來運行一些背景分析。在我最初的測試中,我看到了大量的內存使用情況,所以我基本上創建了一個非常簡單的任務,每2分鐘運行一次,以觀察正在使用多少內存。高內存使用率

該任務非常簡單,analytics_eligbile?方法總是返回false,給定數據現在的位置,所以基本上沒有任何重擊命令被調用。我在開發中的示例數據中有大約200篇文章。發佈has_one analytics_facet。

無論這裏的內部邏輯/業務如何,這項任務所做的唯一事情就是調用analytics_eligible?方法每2分鐘200次。在4小時內,我的物理內存使用量爲110MB,虛擬內存爲200MB。只是爲了做一些簡單的事情!我甚至無法想象,如果在具有實際生產數據的10,000個帖子上執行實際的分析,它將會佔用多少內存!當然,它可能不會運行2分鐘,更像每30分鐘,但我不認爲它會飛。

這是在Ubuntu 10.x 64位上運行ruby 1.9.7,rails 2.3.5。我的筆記本電腦有4GB內存,雙核心CPU。

是軌道真的這麼糟糕還是我做錯了什麼?

Delayed::Worker.logger.info('RAM USAGE Job Start: ' + `pmap #{Process.pid} | tail -1`[10,40].strip) 
Post.not_expired.each do |p| 
    if p.analytics_eligible? 
     #this method is never called 
     Post.find_for_analytics_update(p.id).update_analytics 
    end 
end 
Delayed::Worker.logger.info('RAM USAGE Job End: ' + `pmap #{Process.pid} | tail -1`[10,40].strip) 

Delayed::Job.enqueue PeriodicAnalyticsJob.new(), 0, 2.minutes.from_now 

日誌模型

def analytics_eligible? 
     vf = self.analytics_facet 
     if self.total_ratings > 0 && vf.nil? 
      return true 
     elsif !vf.nil? && vf.last_update_tv > 0 
      ratio = self.total_ratings/vf.last_update_tv 
      if (ratio - 1) >= Constants::FACET_UPDATE_ELIGIBILITY_DELTA 
       return true 
      end 
     end 
     return false 
    end 

回答

18

ActiveRecord相當耗費內存 - 在進行選擇時要非常小心,並且要注意Ruby會自動將塊中的最後一條語句作爲返回值返回,這可能意味着您要傳回的記錄數組作爲結果保存在某個地方,因此不符合GC的條件。

此外,當您調用「Post.not_expired.each」時,您將加載全部 your not_expired帖子到RAM中。一個更好的解決方案是find_in_batches,它一次只能將X記錄加載到RAM中。

修復它可能是簡單的東西如:

def do_analytics 
    Post.not_expired.find_in_batches(:batch_size => 100) do |batch| 
    batch.each do |post| 
     if post.analytics_eligible? 
     #this method is never called 
     Post.find_for_analytics_update(post.id).update_analytics 
     end 
    end 
    end 
    GC.start 
end 

do_analytics 

有幾件事情都發生在這裏。首先,整個事物的作用範圍是防止變量衝突持有塊迭代器的引用。接下來,find_in_batches每次從數據庫中檢索batch_size對象,並且只要不構建對它們的引用,就會在每次迭代運行後變得有資格進行垃圾回收,這將減少總內存使用量。最後,我們在該方法的結尾處​​稱爲GC.start;這迫使GC開始掃描(你不想在實時應用程序中執行此操作,但由於這是後臺工作,因此如果需要額外的300ms運行則可以)。如果返回nil,這也意味着該方法的結果是nil,這意味着我們不會意外掛在從查找程序返回的AR實例上。

使用類似這樣的東西應該可以確保您不會以泄漏的AR對象爲結束,並且應該極大地提高性能和內存使用率。你會想確保你的應用程序沒有在其他地方泄漏(類變量,全局變量和類引用是最糟糕的違規者),但我懷疑這會解決你的問題。所有這一切說,這是一個克倫問題(定期復發的工作),而不是一個DJ問題,在我看來。您可以使用一次性分析解析器,該解析器每隔X分鐘運行一次,分析內容爲script/runner,由cron調用,非常巧妙地清除每次運行的潛在內存泄漏或誤用(因爲整個過程終止)

+0

我唯一要補充的是這個優秀的答案是任何Rails進程都會消耗很多內存 - 你的110mb並不少見。這並不代表你的代碼中有內存泄漏,或者你做了多少處理。處理1000條記錄或10M條記錄將使用相同數量的內存(如果你已經做好了正確的處理)(克里斯解釋過的方式)。 – wuputah 2010-08-28 01:52:04

0

這是一個事實,即紅寶石消耗(和泄漏)的內存。我不知道你是否可以做很多事情,但至少我建議你看看Ruby Enterprise Edition

REE是一個開放源代碼端口,承諾在所有其他優點中「內存減少33%」。我已經使用REE與Passenger合作了近兩年,我感到非常高興。

+0

嗯,我對RoR至今有一定的瞭解,但如果它不好,它真的會讓人失望。我正在嘗試REE,謝謝! – badnaam 2010-08-18 17:48:12

+0

REE的「減少33%內存使用量」的承諾是由於Rails框架本身加載後的進程分叉。在一個單獨的過程中,它不會產生重大影響。 – 2010-08-27 06:23:56

1

如果遇到內存問題,一種解決方案是使用另一種後臺處理技術,如resque。這是由github使用的BG處理。

由於Resque的父/子 架構,即在 完成使用太多 內存釋放內存的工作。沒有不必要的增長

如何?

在某些平臺上,當Resque 工人保留工作立即 叉一個子進程。子 處理作業然後退出。當 孩子已成功退出時, 工作人員保留另一個工作,並且 重複此過程。

您可以在README中找到更多技術細節。

+0

謝謝。這個父/子架構在什麼平臺上工作? – badnaam 2010-08-26 07:59:50

+0

我知道它適用於Linux和OS X.它可能不適用於Windows? – wuputah 2010-08-27 18:09:45

6

正如Chris Heald所建議的那樣,批量加載數據並積極使用垃圾收集器會給你帶來一些非常大的收穫,但人們經常忽略的另一個領域是他們加載的是什麼框架。

加載默認的Rails堆棧會給你ActionController,ActionMailer,ActiveRecord和ActiveResource。如果你正在構建一個Web應用程序,你可能沒有使用所有這些,但你可能使用的是最多。

當你正在構建一個後臺作業,可以避免裝載的東西,你不通過創建一個自定義的環境需要:

# config/environments/production_bg.rb 

config.frameworks -= [ :action_controller, :active_resource, :action_mailer ] 

# (Also include config directives from production.rb that apply) 

每個框架將只是坐在那裏等待永遠不會發送的電子郵件,或永遠不會被調用的控制器。加載它們根本沒有意義。調整您的database.yml文件,將您的後臺作業設置爲在production_bg環境中運行,並且您將擁有更加清潔的平臺。

你可以做的另一件事是直接使用ActiveRecord而不需要加載Rails。這可能就是你爲這個特定操作所需要的一切。我還發現使用像Sequel這樣的輕量級ORM可以使您的後臺工作非常輕量,如果您主要是通過SQL調用來重新組織記錄或刪除舊數據。如果你需要訪問你的模型和他們的方法,你將需要使用ActiveRecord。但是,有時候爲了性能和效率的原因,在純SQL中重新實現簡單的邏輯是值得的。

測量內存使用情況時,唯一需要關注的數字是「真實」內存。虛擬金額包含共享庫,並且這些虛擬金額的費用在使用它們的每個進程之間傳播,即使它們被全部計入每個進程。最後,如果運行一些重要的內容需要100MB的內存,但在三週的工作時間內可以將內存降至10MB,我不明白爲什麼你會打擾。在託管服務提供商中,90MB的內存成本至多約爲60美元/年,這通常比您的時間便宜得多。

Ruby on Rails擁抱更關心您的生產力和時間而不是內存使用的理念。如果你想修剪它,減少它的飲食,你可以做到這一點,但它會花費一點努力。

+0

好點!非常感謝你! – badnaam 2010-08-29 05:54:10