2012-06-27 6 views
3

데이터베이스에서 CSV 파일로 많은 양의 데이터를 내보내려고하는데 아주 오랜 시간이 걸리며 중요한 메모리 문제가 발생할 것을 두려워합니다.레일에서 거대한 CSV 내보내기로 인한 메모리 문제

메모리를 구축하지 않고 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 

답변

2

이궁 :

여기 내 컨트롤러입니다! 예, 나는 더 좋은 길을가집니다. FasterCSV.generate는 문자열을 메모리에 저장하고 블록의 끝에 출력합니다.

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

이렇게하면 각 줄을 파일에 쓰고 send_data 대신 'send_file'을 쓸 수 있습니다. 또는 정말로 send_data를 사용해야한다면 파일 내용을 가져 와서 파일을 작성하는 동안 보내지 말고 보내십시오 (권장하지 않음)

7

전체 csv 파일을 보관해야하므로 하나의 거대한 문자열을 작성해야합니다. 메모리에. 당신은 또한 많은 메모리에 앉아있는 모든 사용자를로드하고 있습니다. 1000 (당신은 단지 몇 백 또는 몇 천 사용자가 있다면 그것은 어떤 차이를하지 않습니다하지만 어느 순간 당신은 아마 2 가지

사용

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

이것은 일괄 적으로 사용자를로드 할 필요가있을 것이다 기본적으로).

또한 전체 내용을 메모리에 버퍼링하는 대신 CSV를 파일로 작성해야합니다. 임시 파일을 만들었다 고 가정하면

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

파일을 CSV로 쓸 수 있습니다. 그런 다음 send_file을 사용하여 보낼 수 있습니다. 이미 파일이 열려 있다면 FasterCSV.new(io)도 작동해야합니다.

마지막으로 레일 3.1 이상에서는 CSV 파일을 만들 때 스트림 할 수 있지만 이전에 시도한 내용이 아닙니다.

+0

제안 된 솔루션에 따라 코드가 변경되었습니다 (마음에 들었는지 알려면 알려주세요).하지만 여전히 오랜 시간이 걸릴 것으로 보입니다. 더 이상 아이디어가 없습니까? 또한 내가 그것을 만들면서 CSV 파일을 스트리밍 할 수있는 방법을 알고 있습니까? 그걸하는 법을 찾지 못하는 것 같아 .. 고마워! –

+0

참고 : 레일즈 3.0.9를 사용하고 있습니다. –

1

또한 csv 생성에 대한 팁 이외에도 데이터베이스 호출을 최적화해야합니다. 필요한 열만 선택하십시오. 당신은 예를 들어 1000 명의 사용자를 위해, 그리고 각 암호, password_salt, 도시, 국가가있는 경우

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

, ... 덜 1000 다음 여러 개체 루비 객체로 만들어 결국 쓰레기 수집, 데이터베이스에서 옮겨진된다.

관련 문제