Ruby 快速处理大量CSV数据的最佳方法

Ruby 快速处理大量CSV数据的最佳方法,ruby,csv,Ruby,Csv,我有需要处理的大型CSV数据集(10M+行)。我还有另外两个文件需要为输出引用,它们包含的数据放大了我们对CSV文件中数百万行的了解。目标是输出一个新的CSV文件,其中每个记录都与其他文件中的附加信息合并 假设大型CSV文件包含交易,但客户信息和账单信息记录在另外两个文件中,我们希望输出一个新的CSV,每个交易都链接到客户ID和帐户ID等 一位同事有一个用Java编写的函数程序来完成这项工作,但速度非常慢。原因是,具有数百万行的CSV文件必须经过多次、多次、多次的处理 我的问题是,是的,我正在着

我有需要处理的大型CSV数据集(10M+行)。我还有另外两个文件需要为输出引用,它们包含的数据放大了我们对CSV文件中数百万行的了解。目标是输出一个新的CSV文件,其中每个记录都与其他文件中的附加信息合并

假设大型CSV文件包含交易,但客户信息和账单信息记录在另外两个文件中,我们希望输出一个新的CSV,每个交易都链接到客户ID和帐户ID等

一位同事有一个用Java编写的函数程序来完成这项工作,但速度非常慢。原因是,具有数百万行的CSV文件必须经过多次、多次、多次的处理

我的问题是,是的,我正在着手,我应该如何在Ruby中实现这一点?目标是让它更快(现在18个多小时,CPU活动很少)

我能把这么多记录载入内存吗?如果是,我应该怎么做


我知道这有点模糊。我只是想寻找一些想法,因为这对我来说有点新鲜。

使用数据库怎么样

将记录放入表中,然后使用联接查询它们


导入可能需要一段时间,但是DB引擎将针对连接和检索部分进行优化…

如果您编写了Java程序,请确保使用NIO库。它们比默认值快得多。我以前使用NIO库处理过500000行的文本文件。

10M+行听起来并没有那么多。如果您可以预加载文件的内容,并将内存中的数据与适当的数据结构相匹配(您在某个时候需要地图),那么您就不必反复运行CSV文件。文件访问速度慢。

两个相当快的选项:

  • 将数据放入sqlite数据库。然后是一个简单的查询,带有一对
    join
    ,它的执行速度比您自己编写的任何查询都要快——SQL非常适合这种任务

  • 假设您的附加CSV文件足够小,可以放入RAM中,您可以使用客户ID作为密钥将所有内容读入哈希,然后在处理包含10+M记录的主文件时查找该哈希。注意,只需要将查找数据放入RAM,主列表可以在小分支中处理


  • 我的经验是,使用Ruby时,准备好实际负载的10倍内存使用率。当然,对于当前的RAM量,如果进程一次只加载一个文件,那么10MB几乎可以忽略不计,即使乘以10:)


    如果您可以一次读取一行(这对于文件实例来说很容易),那么您可以使用FasterCSV并一次写入一行。这将使内存消耗
    O(1
    )而不是
    O(n)
    。但对于10兆字节的文件,如果在任何给定时间只有几个进程,您可能可以将该文件拖到内存中,并一次性将其写入CSV。

    以下是我为处理大型CSV文件而编写的一些ruby代码(在我的例子中约为180mb)

    一个标准的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('/tmp/zendesk_tickets.csv')do|line|
    行数=1000
    lines=FasterCSV.parse(lines.join)rescue next
    商店排队
    行=[]
    结束
    结束
    商店排队
    
    IO.foreach不会将整个文件加载到内存中,而是使用缓冲区逐步完成。当它达到1000行时,它会尝试解析csv并仅插入这些行。一个棘手的部分是“下一步救援”。如果您的CSV有一些跨多行的字段,您可能需要再抓取几行以获得有效的可解析CSV字符串。否则,你所在的那条线可能在一个场地的中间。


    在要点中,您可以看到另一个很好的优化,它在重复键上使用MySQL的更新
    。这允许您批量插入,如果检测到重复的键,它只会覆盖该行中的值,而不是插入新行。您可以将其视为一个查询中的创建/更新。您需要在至少一列上设置一个唯一索引,才能使其正常工作。

    我就是这么想的。你会使用什么样的结构?@NJ:在不知道你的算法是什么的情况下,我甚至不可能开始猜测。你应该多写一些关于所需输出的内容。输入文件中的每条记录(以及辅助查找文件中匹配的一些新列)是否必须只包含一条记录(行)?为什么CSV必须“走很多次”?正如两个(到目前为止)答案所说,数据库是一个很好的解决方案。Postgres或MySQL可以很容易地处理这个问题,包括批量加载文件。他们会很快地导入数据,在您当前看到的18小时内,一旦数据进入数据库,他们就能够动态创建您的输出记录。1000多万条记录对数据库来说算不了什么。:-)一次性slurping 10M+行的问题不在于使用可用的RAM,尽管这是一个可伸缩性问题,而是读取数据时的内存分配问题。我在这里做了一些基准测试,展示了如何快速读取大型文件,然后逐行处理它们。找到基准将是有趣的一部分。啊,找到了。这是我的一部分。我的解释是Ruby读取设置的大小,然后发现它需要越来越多的RAM,所以它必须重新获取内存并移动变量。通过预先定义要读取的数据量,可以预先获取所需的所有数据,然后将传入的数据直接流式传输到缓冲区。哦,“10MB文件”,OP说它们是10M+行而不是字节。是的,记住你可以通过说a=Array来预先分配大小。new(n)#立即分配n个插槽,如果数据被作为数组读取,这会有所帮助。里德不会那样做,它会