/ / बड़े पैमाने पर सीएसवी निर्यात के साथ मेमोरी मुद्दा - रूबी-ऑन-रेल, तेज

रूल्स में विशाल CSV एक्सपोर्ट के साथ मेमोरी इश्यू - रूबी-ऑन-रेल्स, तेजतरंग

मैं एक डेटाबेस से एक सीएसवी फ़ाइल के लिए बड़ी मात्रा में डेटा निर्यात करने की कोशिश कर रहा हूं, लेकिन यह बहुत लंबा समय ले रहा है और मुझे डर है कि मेरे पास प्रमुख मेमोरी समस्याएं हैं।

क्या किसी को मेमोरी बनाने के बिना सीएसवी निर्यात करने का कोई बेहतर तरीका पता है? यदि हां, तो आप मुझे कैसे दिखा सकते हैं? धन्यवाद।

यहाँ मेरा नियंत्रक है:

def users_export
File.new("users_export.csv", "w")           # creates new file to write to
@todays_date = Time.now.strftime("%m-%d-%Y")
@outfile = @todays_date + ".csv"

@users = User.select("id, login, email, last_login, created_at, updated_at")

FasterCSV.open("users_export.csv", "w+") do |csv|
csv << [ @todays_date ]

csv << [ "id","login","email","last_login", "created_at", "updated_at" ]
@users.find_each do |u|
csv << [ u.id, u.login, u.email, u.last_login, u.created_at, u.updated_at ]
end
end

send_file "users_export.csv",
:type => "text/csv; charset=iso-8859-1; header=present",
:disposition => "attachment; filename=#{@outfile}"
end

उत्तर:

उत्तर № 1 के लिए 7

आप एक विशाल स्ट्रिंग का निर्माण कर रहे हैं ताकि आपके पास होमेमोरी में पूरी सीएसवी फाइल रखने के लिए। आप अपने सभी उपयोगकर्ताओं को भी लोड कर रहे हैं जो स्मृति के एक समूह पर भी बैठेंगे। यह जीता है "कोई फर्क नहीं पड़ता अगर आपके पास केवल कुछ सौ या कुछ हजार उपयोगकर्ता हैं, लेकिन कुछ बिंदु आपको शायद 2 चीजें करने की आवश्यकता होगी

उपयोग

User.find_each do |user|
csv << [...]
end

यह उपयोगकर्ताओं को उन सभी के बजाय बैचों (डिफ़ॉल्ट रूप से 1000) में लोड करता है।

आपको मेमोरी में पूरी चीज़ को बफर करने के बजाय csv को एक फाइल में लिखना चाहिए। मान लिया कि आपने एक अस्थायी फ़ाइल बनाई है,

FasterCSV.open("/path/to/file","w") do |csv|
...
end

एक फ़ाइल के लिए अपने सीएसवी लिखेंगे। तब आप उपयोग कर सकते हैं send_file भेजना है। यदि आपके पास पहले से कोई फ़ाइल खुली है, FasterCSV.new(io) काम भी करना चाहिए।

अंत में, रेल 3.1 पर और उच्चतर आप इसे बनाने के रूप में सीएसवी फ़ाइल को स्ट्रीम करने में सक्षम हो सकते हैं, लेकिन इससे पहले कि मैंने कुछ नहीं किया है।


जवाब के लिए 2 № 2

ओह! हां, मेरे पास एक बेहतर तरीका है। FasterCSV.generate मेमोरी में एक स्ट्रिंग संग्रहीत करता है और इसे ब्लॉक के अंत में आउटपुट करता है। आप का उपयोग करने के लिए बेहतर है:

 FasterCSV.open(output_file, "w+") do |row|
row << my_row_data
end

यह प्रत्येक पंक्ति को फाइल करने के लिए लिखेगा तब आप कर सकते हैंsend_data के बजाय "send_file"। या अगर आपको वास्तव में send_data करना चाहिए तो फ़ाइल सामग्री को पकड़ो और फ़ाइल के निर्माण के दौरान इसे रोककर रखने के बजाय भेजें (अभी भी अनुशंसित नहीं है)


उत्तर № 3 के लिए 1

इसके अलावा सीएसवी पीढ़ी के सुझावों के लिए, डेटाबेस के लिए भी कॉल का अनुकूलन करना सुनिश्चित करें। केवल आपको आवश्यक कॉलम चुनें।

@users = User.select("id, login, email, last_login, created_at, updated_at").order("login")
@users.find_each do |user|
...
end

यदि आपके पास उदाहरण के लिए 1000 उपयोगकर्ता हैं, और प्रत्येक के पास हैपासवर्ड, password_salt, शहर, देश, ... फिर कई 1000 ऑब्जेक्ट्स को डेटाबेस से स्थानांतरित किया जाता है, जो रूबी ऑब्जेक्ट्स के रूप में बनाया जाता है और अंत में कचरा एकत्र किया जाता है।