28 July 2016

前言

最近,涉及插入十几万条数据,直接使用 ActiveRecord 的 create 方法,超慢的。找了 ActiveRecord-import 方法。尝试了一下,快了不少。

# 流式的解析 xml 文件
# @note 使用了 Ebayr::Response 中解析 xml 文件, 使用的是 Reader,而不是 Parser 处理
#
# @param {String} file - xml 文件
# @param {Proc} block - 代码块对象
#
# @return
def parse_xml_with_stream(file = 'tmp/ebay_report.xml', &block)
  ActiveSupport::XmlMini.backend = 'Nokogiri'
  reader = Nokogiri::XML::Reader(File.open(file))

  reader.each do |node|
    if node.name == "Recommendations"

      # puts "outer xml: #{node.outer_xml}"
      # puts "node class: #{node.class}"
      # puts "node methods: #{node.public_methods}"
      response = Ebayr::Record.new Ebayr::Response.from_xml(node.outer_xml)

      category = Hashie::Mash.new response

      # 在这里头添加导入数据的处理逻辑

      puts "category: #{category.to_h}"

      block.call category
    end
  end
end

def count_recommendations(file = 'tmp/ebay_report.xml')
  ActiveSupport::XmlMini.backend = 'Nokogiri'
  reader = Nokogiri::XML::Reader(File.open(file))
  count = 1

  reader.each do |node|
    if node.name == "Recommendations"
      count += 1
    end
  end

  count
end

def limit_parse_count(limit_count = 3)
  count = 1

  parse_xml_with_stream do |category|
    count += 1
    puts "count: #{count}"

    break if count > limit_count
  end

  count
end

# 获取并存储 ebay 的属性
#
# @return {Array} categories - 所有的品类的数组
def get_categories
  categories = []

  parse_xml_with_stream do |category|
    categories << category
  end

  categories
end

# 从 xml 文件中,将属性以及属性值的信息导入到
#
# @param {String} xml_file - 需要解析的文件
def import_ebay_specifics_from_xml(xml_file = 'tmp/ebay_report.xml')
  platform = B2cPlatform.ebay

  Profile.parse_xml_with_stream xml_file do |category|
    b2c_category_id = category.recommendations.try(:category_id)

    if b2c_category_id.blank?
      puts "category #{b2c_category_id} is not exists!!!"
      next
    end

    category_id = ProductCategory.get_id_from_cache b2c_category_id, ProductCategory::PlatformType::EBAY

    next if category_id.blank?

    name_recommendations = category.recommendations.try(:name_recommendation)

    next if name_recommendations.blank?

    name_recommendations.each do |item|
      next if item.is_a?(Array)

      product_attribute = ProductAttribute.get_ebay_product_attribute item, platform, category_id

      if product_attribute.product_attribute_values.present?
        next
      elsif item[:value_recommendation].present?
        ProductAttributeValue.import_by_platform_and_product_attribute item[:value_recommendation], platform, product_attribute
      end
    end
  end
end

后记

批量插入数据,使用了 postgresql 支持的批量将数据插入内置方法,非常的方便。

同时插入,一万条数据以上的,在网络状况不好的情况下,会引发 Network Timeout 时间。 后来,发现还是自己的本地的网络的状况不太好。

from /Users/xiajian/.rvm/gems/ruby-2.3.0@camp/gems/activerecord-4.2.5/lib/active_record/connection_adapters/postgresql_adapter.rb:592:in `async_exec'

参考资料

  • http://stackoverflow.com/questions/6675128/how-do-i-use-nokogirixmlreader-to-parse-large-xml-files
  • https://snippets.aktagon.com/snippets/569-how-to-parse-huge-xml-files-with-ruby-and-nokogiri-without-using-too-much-ram-
  • http://blog.gregweber.info/posts/2011-06-03-high-performance-rb-part1
  • http://www.rubydoc.info/github/sparklemotion/nokogiri/master/Nokogiri/XML/SAX/Parser#parse_file-instance_method



傲娇的使用Disqus