如何提高活动记录迭代的性能(rails)

时间:2015-08-31 21:08:12

标签: ruby-on-rails performance

我有以下代码,但处理1000条记录大约需要3分钟。在制作中,我预计会有1 000 000条记录,这种表现对于处理这么多的记录是不可接受的。知道如何加快速度吗?我是Rails的新手,所以还在忙着学习东西。

在下面的示例中,我尝试迭代给定供应商的所有产品,如果产品item_id不在xml Feed中,请将产品ID包含到数组中,我将在下一步中迭代并将产品标记为“存档/不活动”。问题主要在于代码的第一部分,这需要花费太多时间来处理。

self.products.where( :archived => false ).find_each do |p|
   archive = !@xml_feed.css("ITEM_ID").to_s.downcase.include?("<item_id>#{p.item_id}</item_id>")
   archived_product_ids << p.id if archive
end

if archived_product_ids.size > 0
   # update all archived products
   Product.where('id IN (?)', archived_product_ids).update_all( :archived => true, :archived_at => Time.now, :active => false )
   logger.info "Products #{archived_product_ids.to_s} has been archived and deactivated."
end

这是我的控制台中的输出,您可以在处理每1000条记录之间看到3分钟:

[2015-08-31T22:28:18.090063 #28332] DEBUG -- :   Product Load (5.0ms)  SELECT  "products".* FROM "products" WHERE "products"."supplier_id" = $1 AND "products"."archived" = $2  ORDER BY "products"."id" ASC LIMIT 1000  [["supplier_id", 2], ["archived", "f"]]

[2015-08-31T22:31:14.767496 #28332] DEBUG -- :   Product Load (5.3ms)  SELECT  "products".* FROM "products" WHERE "products"."supplier_id" = $1 AND "products"."archived" = $2 AND ("products"."id" > 2513)  ORDER BY "products"."id" ASC LIMIT 1000  [["supplier_id", 2], ["archived", "f"]]

3 个答案:

答案 0 :(得分:0)

我想我会先将复杂表达式赋给变量,所以它只计算一次,并使用pluck来避免实例化所有这些产品对象:

item_ids = @xml_feed.css("ITEM_ID").to_s.downcase
self.products.where( :archived => false ).pluck(:id, :item_id) do |p|
  archive = !item_ids.include?("<item_id>#{p[1]}</item_id>")
  archived_product_ids << p[0] if archive
end

答案 1 :(得分:0)

尝试反转您的搜索。您正在绘制所有记录,并在@xml_feed中查找ID。为什么不尝试在@xml_feed中绘制所有ID,然后查询数据库中的那些?

例如,如果@xml_feed中有大约100个项目,您可以让数据库在一百万条记录中进行所有搜索,以便在查询中匹配id,这是数据库擅长的。

答案 2 :(得分:0)

感谢所有有价值的提示。我能够将处理1 000条记录的时间从3分钟减少到5秒,这是完美的!每个供应商平均拥有大约8k记录和不同的xml供稿源,因此我现在可以每天单独运行一个cron作业来更新每个供应商的产品。对于所有(100万)和一名工人,这应该在1.5小时内完成,这是可以接受的。

# archive products if they are not present in the xml feed
item_ids = @xml_feed.css("ITEM_ID").to_s
self.products.where( :archived => false ).pluck(:id, :item_id).each do |p|
    archive = !item_ids.include?("<ITEM_ID>#{p[1]}</ITEM_ID>")
    if archive
        archived_product_ids << p[0]
        archived_products += 1
        new_import_record.update_attributes(archived_products: archived_products)
    end
end