【问题标题】:Speed up csv import加快 csv 导入
【发布时间】:2012-08-23 09:09:52
【问题描述】:

我想导入大量的 cvs 数据(不是直接到 AR,而是在一些 fetch 之后),我的代码很慢。

def csv_import 
    require 'csv'
    file = File.open("/#{Rails.public_path}/uploads/shate.csv")
    csv = CSV.open(file, "r:ISO-8859-15:UTF-8", {:col_sep => ";", :row_sep => :auto, :headers => :first_row})

    csv.each do |row|
      #ename,esupp= row[1].split(/_/) 
      #(ename,esupp,foo) = row[1]..split('_')
      abrakadabra = row[0].to_s()
      (ename,esupp) = abrakadabra.split(/_/)
      eprice = row[6]
      eqnt = row[1]
      # logger.info("1) ")
      # logger.info(ename)
      # logger.info("---")
      # logger.info(esupp)
      #----
      #ename = row[4]
      #eprice = row[7]
      #eqnt = row[10]
      #esupp = row[12]

        if ename.present? && ename.size>3
        search_condition = "*" + ename.upcase + "*"     

        if esupp.present?
          #supplier = @suppliers.find{|item| item['SUP_BRAND'] =~ Regexp.new(".*#{esupp}.*") }
          supplier = Supplier.where("SUP_BRAND like ?", "%#{esupp}%").first
          logger.warn("!!! *** supp !!!")
          #logger.warn(supplier)
        end

        if supplier.present?

          @search = ArtLookup.find(:all, :conditions => ['MATCH (ARL_SEARCH_NUMBER) AGAINST(? IN BOOLEAN MODE)', search_condition.gsub(/[^0-9A-Za-z]/, '')])
          @articles = Article.find(:all, :conditions => { :ART_ID => @search.map(&:ARL_ART_ID)})
          @art_concret = @articles.find_all{|item| item.ART_ARTICLE_NR.gsub(/[^0-9A-Za-z]/, '').include?(ename.gsub(/[^0-9A-Za-z]/, '')) }

          @aa = @art_concret.find{|item| item['ART_SUP_ID']==supplier.SUP_ID} #| @articles
          if @aa.present?
            @art = Article.find_by_ART_ID(@aa)
          end

          if @art.present?
            @art.PRICEM = eprice
            @art.QUANTITYM = eqnt
            @art.datetime_of_update = DateTime.now
            @art.save
          end

        end
        logger.warn("------------------------------")       
      end

      #logger.warn(esupp)
    end
 end

即使我删除并只得到这个,它也很慢。

def csv_import 
    require 'csv'
    file = File.open("/#{Rails.public_path}/uploads/shate.csv")
    csv = CSV.open(file, "r:ISO-8859-15:UTF-8", {:col_sep => ";", :row_sep => :auto, :headers => :first_row})

    csv.each do |row|
    end
end

有人可以帮我提高使用 fastercsv 的速度吗?

【问题讨论】:

  • 这不会影响速度,但是你没有关闭file使用File.readlines("/file")。这样您就不必担心文件处于打开状态。
  • @TheWho 不懂你,可以举个详细的例子吗?
  • 如果你运行 File.open 那么你需要关闭文件。您不想泄露打开的文件。 stackoverflow.com/questions/4795447/…
  • @TheWho 如果我使用 CSV.foreach("/#{Rails.public_path}/uploads/hshatem2.csv", {:col_sep => ',', :row_sep => 将如何关闭文件:自动, :headers => :first_row}) 做 |行 |
  • 如果您在块中打开文件(例如 File.open(file) { ... }),则该文件将自动关闭。

标签: ruby optimization csv fastercsv


【解决方案1】:

查看 Gem smarter_csv!它可以分块读取 CSV 文件,然后您可以创建 Resque 作业以进一步处理这些块并将其插入到数据库中。

https://github.com/tilo/smarter_csv

【讨论】:

    【解决方案2】:

    我不认为它会变得更快。

    也就是说,一些测试表明大部分时间都花在了转码上(我的测试用例大约占 15%)。因此,如果您可以跳过它(例如,已经在 UTF-8 中创建 CSV),您会看到一些改进。

    此外,根据ruby-doc.org,用于读取 CSV 的“主要”接口是 foreach,所以这应该是首选:

    def csv_import
      import 'csv'
      CSV.foreach("/#{Rails.public_path}/uploads/shate.csv", {:encoding => 'ISO-8859-15:UTF-8', :col_sep => ';', :row_sep => :auto, :headers => :first_row}) do | row |
        # use row here...
      end
    end
    

    更新

    您也可以尝试将解析拆分为多个线程。我在尝试使用此代码时实现了一些性能提升(标题被遗漏的处理):

    N = 10000
    def csv_import
      all_lines = File.read("/#{Rails.public_path}/uploads/shate.csv").lines
      # parts will contain the parsed CSV data of the different chunks/slices
      # threads will contain the threads
      parts, threads = [], []
      # iterate over chunks/slices of N lines of the CSV file
      all_lines.each_slice(N) do | plines |
        # add an array object for the current chunk to parts
        parts << result = []
        # create a thread for parsing the current chunk, hand it over the chunk 
        # and the current parts sub-array
        threads << Thread.new(plines.join, result) do  | tsrc, tresult |
          # parse the chunk
          parsed = CSV.parse(tsrc, {:encoding => 'ISO-8859-15:UTF-8', :col_sep => ";", :row_sep => :auto})
          # add the parsed data to the parts sub-array
          tresult.replace(parsed.to_a)
        end
      end
      # wait for all threads to finish
      threads.each(&:join)
      # merge all the parts sub-arrays into one big array and iterate over it
      parts.flatten(1).each do | row |
        # use row (Array)
      end
    end
    

    这会将输入分成 10000 行的块,并为每个块创建一个解析线程。每个线程都被移交给数组parts 中的一个子数组,用于存储其结果。当所有线程都完成时(在threads.each(&amp;:join) 之后),parts 中所有块的结果都是联合的,仅此而已。

    【讨论】:

    • 嗯,你能根据我的问题重写你的更新吗? ...你可以获得+50
    • 另外,如何将我的 csv 转换为 utf8 以便 ruby​​ 理解它?我尝试 utf8,一切正常,但是当我的 utf8-doc 中出现俄语单词(会有很多)时,它会发送 utf8 错误...如何解决?
    • 对不起,我不明白这个问题。 Ruby 读取 UTF-8 CSV 是否有问题或 CSV 有不同的编码?也许你应该发布另一个 Stackoverflow 问题。
    • 另外,请为我的代码编辑你的更新代码......并解释它)
    • 做到了。如果 Ruby 无法正确读取 UTF-8 编码的 CSV(包含俄语),那么这是一个 Ruby 错误。我对此表示怀疑。您应该针对该问题打开一个新问题。
    【解决方案3】:

    我很好奇文件有多大,有多少列。

    使用 CSV.foreach 是首选方式。在您的应用程序运行时查看内存配置文件会很有趣。 (有时速度慢是由于打印,所以请确保您不要做超出您需要的操作)

    您也许可以对其进行预处理,并排除任何没有 esupp 的行,因为看起来您的代码只关心这些行。此外,您可以截断任何您不关心的右侧列。

    另一种技术是收集独特的组件并将它们放入哈希中。似乎您多次触发相同的查询。

    您只需要对其进行概要分析,然后查看它在哪里花费时间。

    【讨论】:

    • 我知道通过调查结果进行查询需要时间,但为什么这个 csv 会打开这么久......作为我没有编码的变体,但如何解决呢?我也打开了这个问题,但没有人帮忙
    • 好吧,如果我们不知道重要的细节,就很难提供帮助。去除敏感数据,并通过我们可以检查的要点中的重要部分。让帮助你变得更容易,我相信你会得到一些帮助。
    【解决方案4】:

    顾名思义,更快的 CSV 就是更快:)

    http://fastercsv.rubyforge.org

    另见。了解更多信息

    Ruby on Rails Moving from CSV to FasterCSV

    【讨论】:

    • 这是标准的 ruby​​ 1.9 fastercsv !!!一些管理员@sawa 从我的问题中删除了这个重要的词!!!
    • @PavelBY 你只是把这个词放在括号里,与其他句子隔离,不清楚它的意思。
    • @sawa 请像我以前那样做!但照你说的做 => 清晰合理
    猜你喜欢
    • 1970-01-01
    • 2013-04-02
    • 1970-01-01
    • 2019-05-02
    • 1970-01-01
    • 2017-01-07
    • 1970-01-01
    • 1970-01-01
    • 2023-01-18
    相关资源
    最近更新 更多