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



我有以下代码,但是处理1000条记录大约需要3分钟。在生产中,我期望有1,000,000条记录,这种性能对于处理如此数量的记录是不可接受的。有什么办法能快一点吗?我是Rails的新手,所以还在不断学习。

在下面的例子中,我试图迭代给定供应商的所有产品,如果产品item_id不在xml提要中,则将产品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"]]

我想我会首先将复杂的表达式赋值给一个变量,因此它只计算一次,并使用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

尝试反向搜索。您正在提取所有记录,并在@xml_feed中查找id。为什么不尝试在@xml_feed中提取所有id,然后在数据库中查询这些id呢?

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

谢谢大家的宝贵建议。我能够将处理1000条记录的时间从3分钟减少到5秒,这是完美的!每个供应商平均有大约8k条记录和不同的xml提要源,所以我现在可以运行cron作业来每天分别为每个供应商更新产品。这应该在1.5小时内完成所有(100万)和一个工人,这是可以接受的。

# 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

最新更新