如何将我的插入物分成小块?

时间:2013-10-28 16:25:37

标签: mysql ruby-on-rails ruby import

这是我的导入脚本。我有大约40万条记录。我的连接在插入记录时关闭。如何将其拆分成较小的碎片?

  def extract_to_database(collection)
    tmp       = []
    type      = K
    inserts = []

    collection.each_with_index do |line, i|
      _type    = line.strip
      _changed = TYPES.include? _type

      if _changed && i > 0
        case type
        when K then @k << tmp
        when F then @f << tmp
        when FB then @f << tmp
        when I, D
          pharmaceutic = Pharmaceutic.find_by pzn: tmp[PZN]
          if pharmaceutic
            inserts.push "(#{pharmaceutic.id}, '#{tmp[UNIT]}', '#{tmp[DOSE]}')"
          end
        end

        tmp  = []
        type = _type
      end

      tmp << clean(line)
    end
    sql = "INSERT INTO pharmaceutic_dosages (`pharmaceutic_id`, `unit`, `dose`) VALUES #{inserts.join(", ")}"
    CONN.execute sql
  end

3 个答案:

答案 0 :(得分:1)

您的问题中缺少许多必需的信息,特别是您正在使用的DBM,您的超时值设置为什么,代码中您打开数据库连接的位置,实际需要多长时间用于处理400,000条记录等的代码。所有这些都会影响您工作的成败。

这段代码引起了我的注意:

sql = "INSERT INTO pharmaceutic_dosages (`pharmaceutic_id`, `unit`, `dose`) VALUES #{inserts.join(", ")}"
CONN.execute sql

您在哪里打开与数据库的连接?为什么打开它然后不立即使用它?数据库配置为在一段时间后关闭未使用的连接,因为连接会占用内存和CPU时间。连接后,发送更新/删除/插入,然后关闭它。如果你经常连接/关闭,你只需要一个持久的连接,这对DBM /服务器也是一个很大的CPU消耗。

而不是在计算时打开然后保持连接,等待打开它直到数据准备好,然后打开它并立即发送。我不知道你正在使用什么驱动程序或DBM,所以请考虑这个伪代码:

sql = "INSERT INTO pharmaceutic_dosages (`pharmaceutic_id`, `unit`, `dose`) VALUES #{inserts.join(", ")}"    
CONN = connect_to_database(...)
CONN.execute sql
CONN.close

这样做,我怀疑你会看到超时错误。

答案 1 :(得分:0)

如果连接在插入记录时关闭,为什么不让脚本将SQL语句写入文件,然后对数据库运行这些插入?

所以:

  1. 按照上面的脚本生成SQL语句。
  2. 将它们写入文件(本地或S3)。
  3. 复制该文件的内容并在数据库控制台中运行。
  4. 这样看似可以防止OOM错误。

答案 2 :(得分:0)

前段时间我有同样的问题。我找到的最佳解决方案是使用resque的后台作业。请参阅this回答。

我还使用resque-status向用户显示WIP。