我有需要处理的大型CSV数据集(10M +行)。我还有两个需要为输出引用的文件 - 它们包含的数据可以放大我们对CSV文件中数百万行的了解。目标是输出一个新的CSV文件,其中每个记录与其他文件中的附加信息合并。
想象一下,大型CSV文件有交易,但客户信息和账单信息记录在另外两个文件中,我们希望输出一个新的CSV,每个交易都链接到客户ID和账户ID等。
一位同事有一个用Java编写的功能程序来执行此操作,但速度非常慢。原因是具有数百万行的CSV文件显然需要经过许多次,很多次。
我的问题是 - 是的,我正在努力 - 我应该如何在Ruby中解决这个问题?目标是让它更快(现在只需18个小时,CPU活动很少)
我可以将这么多记录加载到内存中吗?如果是这样,我该怎么做?
我知道这有点模糊。只是寻找想法,因为这对我来说有点新鲜。
答案 0 :(得分:30)
这是我编写的一些ruby代码来处理大型csv文件(在我的情况下约为180mb)。
https://gist.github.com/1323865
标准的FasterCSV.parse将其全部存入内存需要一个多小时。这让它减少到大约10分钟。
相关部分是:
lines = []
IO.foreach('/tmp/zendesk_tickets.csv') do |line|
lines << line
if lines.size >= 1000
lines = FasterCSV.parse(lines.join) rescue next
store lines
lines = []
end
end
store lines
IO.foreach不会将整个文件加载到内存中,只是使用缓冲区逐步完成。当它到达1000行时,它会尝试解析csv并只插入那些行。一个棘手的部分是“下一个救援”。如果您的CSV有一些跨越多行的字段,您可能需要再获取几行来获取有效的可解析csv字符串。否则,你所在的线可能位于一个字段的中间。
在要点中,您可以看到另一个使用MySQL更新ON DUPLICATE KEY
的优秀优化。这允许您批量插入,如果检测到重复键,它只会覆盖该行中的值而不是插入新行。您可以将其视为一个查询中的创建/更新。您需要在至少一列上设置唯一索引才能使其生效。
答案 1 :(得分:2)
如何使用数据库。
将记录塞入表中,然后使用连接查询它们。
导入可能需要一段时间,但数据库引擎将针对连接和检索部分进行优化...
答案 2 :(得分:2)
10M +行听起来并不像那样。如果你可以预加载文件的内容并将内存中的数据与合适的数据结构相匹配(你需要在某些时候使用地图),你就不必一遍又一遍地运行CSV文件。文件访问权限为 SLOW 。
答案 3 :(得分:2)
两个相当快的选择:
将您的数据放入sqlite DB。然后,这是一对简单的查询,其中join
对的执行速度比您自己编写的任何速度都要快 - SQL非常适合这类任务。
假设您的其他CSV文件足够小以适应RAM,您可以使用客户ID作为密钥将所有内容读入哈希值,然后在处理具有10 + M记录的主文件时查找该哈希值。请注意,只需要将查找数据放入RAM中,主列表可以在小分支中处理。
答案 4 :(得分:1)
我的经验是,使用Ruby,准备实际有效负载的内存使用量约为10倍。当然,对于当前的RAM量,如果进程一次只加载一个文件,即使乘以10,10MB也几乎可以忽略不计:)
如果您可以一次读取一行(使用File实例很容易),您可以使用FasterCSV并一次写一行。这将使内存消耗O(1
)而不是O(n)
。但是对于10兆字节的文件,您可以将该文件粘贴到内存中,并在一次通过中将其写入CSV,在任何给定时间只给出几个进程。
答案 5 :(得分:-1)
如果您编写了Java程序,请确保使用NIO库。它们比默认速度快。我之前使用NIO库处理了500,000行的文本文件。