2011-07-12 23 views
30

में सहायता नहीं करता है मेरे पास एक रेल ऐप है जो एक MySQL डेटाबेस में बड़ी (लाखों) रिकॉर्ड्स को संसाधित करता है। एक बार यह काम शुरू हो जाने के बाद, इसकी स्मृति का उपयोग प्रति सेकंड 50 एमबी की रफ्तार से बढ़ता है। ओंक जैसे टूल्स के साथ मैं मूल कारण को एक लूप को सीमित करने में सक्षम था जो डेटाबेस में एक बड़ी तालिका में सभी रिकॉर्ड्स के माध्यम से जाता है।रेल मेमोरी रिसाव पर रूबी; find_each

मैं समझता हूं कि अगर मैं Person.all.each जैसे कुछ का उपयोग करता हूं, तो सभी रिकॉर्ड स्मृति में लोड हो जाएंगे। हालांकि अगर मैं find_each पर स्विच करता हूं, तो भी मुझे वही स्मृति समस्या दिखाई देती है। समस्या को और अलग करने के लिए मैंने निम्न परीक्षण नियंत्रक बनाया है, जो रिकॉर्ड के माध्यम से लूपिंग के अलावा कुछ भी नहीं करता है। मुझे लगता है कि find_each प्रत्येक बार स्मृति में केवल छोटी संख्या में ऑब्जेक्ट रखता है, लेकिन मेमोरी उपयोग रैखिक रूप से बढ़ता है क्योंकि यह निष्पादित होता है।

class TestController < ApplicationController 
    def memory_test 
    Person.find_each do |person| 
    end 
end 

मुझे संदेह है कि इसे क्वेरी परिणाम कैशिंग ActiveRecord के साथ करना है। लेकिन मैंने अपनी पर्यावरण सेटिंग्स की जांच की और मेरे पास सभी कैशिंग संबंधित विकल्प विकास में झूठे सेट हैं (मैं रेल द्वारा बनाई गई डिफ़ॉल्ट सेटिंग्स का उपयोग कर रहा हूं)। मैंने कुछ खोज ऑनलाइन की लेकिन मुझे कोई समाधान नहीं मिला।

मैं रेल उपयोग कर रहा हूँ 3.1.0 RC1 और गहरे लाल रंग का 1.9.2

धन्यवाद!

+0

मुझे लगता है कि ActiveRecord में 'find_in_batches' नामक एक फ़ंक्शन है। हो सकता है कि यह स्मृति प्रकोप को नियंत्रित करने में मदद करेगा। – rubish

+0

मैं यह भी सोच रहा था, हालांकि, ऐसा लगता है कि 'find_each' कवर के तहत 'find_in_batches' का उपयोग करता है। हो सकता है कि प्रत्येक व्यक्तिगत पंक्ति बड़ी हो और ': batch_size' विकल्प (1000 पंक्तियों के लिए डिफ़ॉल्ट) से लाभ हो सकती है – Brian

+0

वास्तव में कोड क्या है जो इसे प्रत्येक रिकॉर्ड के माध्यम से लूप करने की आवश्यकता है? – Maran

उत्तर

37

मैं इसे स्वयं समझने में सक्षम था। बदलने के लिए दो जगहें हैं।

सबसे पहले, पहचान मैप अक्षम करें। config/application.rb

config.active_record.identity_map = false 

दूसरा में, uncached का उपयोग पाश

class MemoryTestController < ApplicationController 
    def go 
    ActiveRecord::Base.uncached do 
     Person.find_each do |person| 
     # whatever operation 
     end 
    end 
    end 
end 

अब मेरी स्मृति उपयोग नियंत्रण में है लपेट के लिए। उम्मीद है कि यह अन्य लोगों की मदद करता है।

+0

धन्यवाद आपको धन्यवाद कि आपको लगता है कि –

+0

जब भी मैं बड़ी मात्रा में डेटा के माध्यम से लूप करता हूं तो क्या मुझे विचारों में इसका उपयोग करना चाहिए? – bcackerman

+0

प्रलेखन के आधार पर, ['identity_map' डिफ़ॉल्ट रूप से अक्षम है] (http://apidock.com/rails/ActiveRecord/IdentityMap), इसलिए आपको केवल यह सुनिश्चित करने की आवश्यकता है कि यह आपके वर्तमान कॉन्फ़िगरेशन में सत्य पर सेट न हो (कम से कम मैं सोचता हूं- इसे स्वयं परीक्षण करने के बारे में)। – MaxGabriel

3

ActiveRecord के रूप में अच्छा है, यह सभी समस्याओं के लिए सबसे अच्छा उपकरण नहीं है। मैं आपके मूल डेटाबेस एडाप्टर को छोड़ने और उस स्तर पर काम करने की सलाह देता हूं।

+0

एसक्यूएल के तहत सभी काम नहीं किए जा सकते हैं, ज्यादातर समय हमें कुछ जटिल व्यापार तर्कों को संसाधित करने की आवश्यकता होती है ... – linjunhalida

2

find_eachfind_in_batches को हुड के नीचे 1000 के बैच आकार के साथ कॉल करता है।

बैच में सभी रिकॉर्ड बनाए जाएंगे और बैच को संसाधित होने तक स्मृति में बनाए रखा जाएगा।

अपने रिकॉर्ड के बड़े हैं या अगर वे (जैसे has_many अपने आइटम के सभी किसी भी समय आप इसका इस्तेमाल कैश) प्रॉक्सी संग्रह के माध्यम से स्मृति का उपयोग करते हैं, तो आप भी एक छोटे बैच आकार की कोशिश कर सकते हैं:

Person.find_each batch_size: 100 do |person| 
    # whatever operation 
    end 

आप समय-समय पर GC.start को मैन्युअल रूप से कॉल करने का प्रयास कर सकते हैं (उदाहरण के लिए प्रत्येक 300 आइटम)

संबंधित मुद्दे