gpt4 book ai didi

ruby-on-rails - rails - 导出一个巨大的 CSV 文件会消耗生产中的所有 RAM

转载 作者:行者123 更新时间:2023-12-04 05:57:46 24 4
gpt4 key购买 nike

所以我的应用程序导出了一个 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 交换内存。

所以我的问题是:
  • 究竟是什么消耗了这么多内存?是 CSV 生成还是与 DB 的通信?
  • 我是不是做错了什么而错过了内存泄漏?或者这只是图书馆的工作方式?
  • 有没有办法在不重启 puma workers 的情况下释放内存?

  • 提前致谢!

    最佳答案

    您应该使用 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/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com