【问题标题】:optimizing reading database and writing to csv file优化读取数据库和写入 csv 文件
【发布时间】:2014-01-16 11:34:25
【问题描述】:

我正在尝试从数据库中读取大量单元格(超过 100.000 个)并将它们写入 VPS Ubuntu 服务器上的 csv 文件。碰巧服务器没有足够的内存。

我正在考虑一次读取 5000 行并将它们写入文件,然后再读取 5000 行,等等。

我应该如何重构我当前的代码,以免内存被完全消耗掉?

这是我的代码:

def write_rows(emails)

  File.open(file_path, "w+") do |f|
    f << "email,name,ip,created\n"
    emails.each do |l|
      f << [l.email, l.name, l.ip, l.created_at].join(",") + "\n"
    end
  end
end

该函数由 sidekiq worker 调用:

write_rows(user.emails)

感谢您的帮助!

【问题讨论】:

    标签: ruby-on-rails ruby csv sidekiq


    【解决方案1】:

    这里的问题是,当你调用emails.eachActiveRecord 时,ActiveRecord 会从数据库中加载所有记录并将它们保存在内存中,为避免这种情况,你可以使用方法find_each

    require 'csv'
    
    BATCH_SIZE = 5000
    
    def write_rows(emails)
      CSV.open(file_path, 'w') do |csv|
    
        csv << %w{email name ip created}
    
        emails.find_each do |email|
          csv << [email.email, email.name, email.ip, email.created_at]
        end
      end
    end
    

    默认find_each一次加载1000条记录,如果你想加载5000条记录,你必须将选项:batch_size传递给find_each

    emails.find_each(:batch_size => 5000) do |email|
      ...
    

    有关find_each 方法(以及相关的find_in_batches)的更多信息可以在Ruby on Rails Guides 上找到。

    我使用CSV 类来编写文件,而不是手动连接字段和行。这不是性能优化,因为写入文件不应该是这里的瓶颈。

    【讨论】:

    • 谢谢.. 写入 acsv 文件怎么样? csv gem 会优化写入文件吗?
    • @Aljaz 不是,我使用CSV 只是为了避免加入字段/行。 csv 不是宝石,它来自Ruby stdlib
    • @Aljaz 但是,CSV 模块将确保您的所有值都正确转义。如果您的数据库中的任何值都有可能包含逗号或换行符(即,如果您接受用户输入并且没有严格的验证来拒绝这些字符),您应该使用 CSV 模块而不是这样做“手动。”老实说,100,000 行并不是很多,而 CSV 模块(自 1.9.3 以来基于 FasterCSV)将很快完成。
    • find_each 对我没有帮助。由于某种原因,它使用 700k 条记录使用了 1GB 内存。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多