所以我的应用程序导出了一个 11.5 MB 的 CSV 文件,并且基本上使用了所有从未被释放的 RAM。
CSV 的数据取自数据库,在上述情况下,整个内容都被导出。
我以下列方式使用 Ruby 2.4.1 标准 CSV 库:export_helper.rb
:
CSV.open('full_report.csv', 'wb', encoding: UTF-8) do |file|
data = Model.scope1(param).scope2(param).includes(:model1, :model2)
data.each do |item|
file << [
item.method1,
item.method2,
item.methid3
]
end
# repeat for other models - approx. 5 other similar loops
end
然后在 Controller 中:
generator = ExportHelper::ReportGenerator.new
generator.full_report
respond_to do |format|
format.csv do
send_file(
"#{Rails.root}/full_report.csv",
filename: 'full_report.csv',
type: :csv,
disposition: :attachment
)
end
end
在一次请求之后,puma 进程会加载整个服务器 RAM 的 55% 并保持这种状态,直到最终完全耗尽内存。
例如在 this article 中生成一百万行 75 MB CSV 文件只需要 1 MB 的 RAM。但是不涉及数据库查询。
服务器有 1015 MB RAM + 400 MB 交换内存。
所以我的问题是:
提前致谢!
最佳答案
您应该使用 each
而不是 find_each
,它专门用于此类情况,因为它将批量实例化模型并在之后释放它们,而 each
将一次实例化所有模型。
CSV.open('full_report.csv', 'wb', encoding: UTF-8) do |file|
Model.scope1(param).find_each do |item|
file << [
item.method1
]
end
end
此外,您应该流式传输 CSV 而不是将其写入内存或磁盘,然后再将其发送到浏览器:format.csv do
headers["Content-Type"] = "text/csv"
headers["Content-disposition"] = "attachment; filename=\"full_report.csv\""
# streaming_headers
# nginx doc: Setting this to "no" will allow unbuffered responses suitable for Comet and HTTP streaming applications
headers['X-Accel-Buffering'] = 'no'
headers["Cache-Control"] ||= "no-cache"
# Rack::ETag 2.2.x no longer respects 'Cache-Control'
# https://github.com/rack/rack/commit/0371c69a0850e1b21448df96698e2926359f17fe#diff-1bc61e69628f29acd74010b83f44d041
headers["Last-Modified"] = Time.current.httpdate
headers.delete("Content-Length")
response.status = 200
header = ['Method 1', 'Method 2']
csv_options = { col_sep: ";" }
csv_enumerator = Enumerator.new do |y|
y << CSV::Row.new(header, header).to_s(csv_options)
Model.scope1(param).find_each do |item|
y << CSV::Row.new(header, [item.method1, item.method2]).to_s(csv_options)
end
end
# setting the body to an enumerator, rails will iterate this enumerator
self.response_body = csv_enumerator
end
关于ruby-on-rails - rails - 导出一个巨大的 CSV 文件会消耗生产中的所有 RAM,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52833450/