Ruby-on-Rails 3.2:导出包含大型数据集(100,000 条记录)的 CSV
Posted
技术标签:
【中文标题】Ruby-on-Rails 3.2:导出包含大型数据集(100,000 条记录)的 CSV【英文标题】:Ruby-on-Rails 3.2: Export a CSV with a large data set (100,000 records) 【发布时间】:2012-05-02 00:03:54 【问题描述】:简介
我的应用有多个表,有些有关联,有些没有关联。
有些表格需要容纳大约 100,000 个条目。
该应用在 Ruby 1.9 上使用 Rails 3.2,并托管在 Heroku 上。如果需要,我可以联系工作人员。
问题中的要求
应用程序的一个重要要求是允许用户将数据导出为 CSV - 对此的要求是允许用户过滤他们想要导出的数据,但我现在并不担心这一点,因为您从下面的数据中可以看出,我已经硬编码了要导出的数据,但这确实排除了创建一个 rake 任务来导出整个表。
此外,实现的方法必须考虑到允许被多个表使用,以避免不必要的代码重复。
当前解决方案
我正在我的应用程序中实现延迟作业并在作业中执行 CSV 生成。在执行此操作时,我正在关注来自“abdullah”的http://www.ayokasystems.com/blog/delegating-long-running-jobs-in-rails/ 的解决方案。
我们的想法是生成 CSV 格式的数据并将其保存在 UserJobs 表的 LONGTEXT 字段中,以便用户在完成后和以后下载。
问题
上述教程中使用的方法在我的应用程序中运行良好,直到我一次运行 100,000 条记录的作业。为了克服这个问题,我尝试将很酷的 find_each 函数添加到 perform 方法中,但是延迟的作业工作者每次尝试处理它时都会报告一个错误:
[Worker(host:*** pid:18637)] ReportJob failed with NoMethodError: undefined method `each' for #<Title:0x007ff20c1ec1b0> - 0 failed attempts
[Worker(host:*** pid:18637)] ReportJob failed with NoMethodError: undefined method `each' for #<Title:0x007ff20ec47f18> - 1 failed attempts
[Worker(host:*** pid:18637)] 2 jobs processed at 10.5219 j/s, 2 failed ...
我的 perform 方法代码是:
def perform
Title.find_each do |titles|
csv_data = CSV.generate do |csv|
titles.each do |t|
csv << t.to_csv
end
end
user_job = UserJob.find(user_job_id)
user_job.update_attribute :data, csv_data
end
end
任何人都可以看到问题可能是什么,我想我在循环事物的方式上犯了一个愚蠢的错误。
我非常愿意接受有关如何完成相关要求的任何其他建议,但请记住我对 Heroku 的限制。
【问题讨论】:
已经对我的代码进行了编辑,因为我刚刚看到我忘记将 CSV.generate 分配给 csv_data 以用于更新记录。 【参考方案1】:您正在尝试使用 each 进行迭代,但在这种情况下,标题是标题的实例(不是数组)。
csv_vals = []
columns = [:name, :release_date, :studio]
Title.find_each(:select => columns) do |title|
columns.each |value| csv_vals << "#title[value]"
end
# comma separated string
csv_string = csv_vals.join(',')
有更优雅的方式来制定 CSV 字符串,但我懒得尝试。
重要的是您只在需要的列上执行 SELECT。对于 100 000 条记录,这会大大减少带宽 DB 通信。只需 find_each,您就可以获得每一行的所有列,而您不需要它们。
【讨论】:
感谢您的回答,但另一个要求是此方法将来会接受来自其他表的数据,因此我不想实现选择。我知道在问题中我列出了会导致您做出假设的特定字段,但这仅供参考,我已修改问题以解决此问题。但是很好的答案,并且会记住这种技术以备不时之需。 您可以将列参数作为方法参数,代码将适用于不同的列。无论如何请记住,如果您要查找 find_each 并且有很多记录,请使用 select :) 对不起,我会,但我还没有足够的代表来做这件事。我会尽快做的! 我已经尝试了代码,但现在我在 ReportJob 的工作人员中遇到了一个不同的错误,原因是 'ActiveRecord::StatementInvalid: mysql2::Error: MySQL server has gone away' 这会破坏工作人员,这在对数据库进行更新时发生,在我放置在您的代码块下方的以下代码行中 - user_job.update_attribute :data, csv_string 您正在使用delayed_jobs 表并尝试写入列数据(那里没有数据列),除非您手动添加,默认情况下没有该列。【参考方案2】:find_each
为块生成单个记录,而不是集合,因此您在单个记录上调用 each
时出错。看看find_in_batches,或者修复你的代码以使用单个记录:
Title.find_each do |title|
CSV.generate do |csv|
csv << title.to_csv
end
user_job = UserJob.find(user_job_id)
user_job.update_attribute :data, csv_data
end
【讨论】:
顺便说一句,这只是为了解决您的特定错误 - 我不会对该代码的效率发表意见。 感谢您选择这个并引用 api!以上是关于Ruby-on-Rails 3.2:导出包含大型数据集(100,000 条记录)的 CSV的主要内容,如果未能解决你的问题,请参考以下文章