【发布时间】:2013-09-30 17:29:39
【问题描述】:
在我们的 Rails 3.2.13 应用程序(Ruby 2.0.0 + Heroku 上的 Postgres)中,我们经常从 API 检索大量订单数据,然后我们需要更新或创建数据库中的每个订单,因为以及协会。一个订单创建/更新自己加上大约。 10-15 个关联对象,我们一次最多导入 500 个订单。
下面的代码有效,但问题是它在速度方面根本没有效率。创建/更新 500 条记录大约需要。 1 分钟,生成 6500+ db 查询!
def add_details(shop, shopify_orders)
shopify_orders.each do |shopify_order|
order = Order.where(:order_id => shopify_order.id.to_s, :shop_id => shop.id).first_or_create
order.update_details(order,shopify_order,shop) #This calls update_attributes for the Order
ShippingLine.add_details(order, shopify_order.shipping_lines)
LineItem.add_details(order, shopify_order.line_items)
Taxline.add_details(order, shopify_order.tax_lines)
Fulfillment.add_details(order, shopify_order.fulfillments)
Note.add_details(order, shopify_order.note_attributes)
Discount.add_details(order, shopify_order.discount_codes)
billing_address = shopify_order.billing_address rescue nil
if !billing_address.blank?
BillingAddress.add_details(order, billing_address)
end
shipping_address = shopify_order.shipping_address rescue nil
if !shipping_address.blank?
ShippingAddress.add_details(order, shipping_address)
end
payment_details = shopify_order.payment_details rescue nil
if !payment_details.blank?
PaymentDetail.add_details(order, payment_details)
end
end
end
def update_details(order,shopify_order,shop)
order.update_attributes(
:order_name => shopify_order.name,
:order_created_at => shopify_order.created_at,
:order_updated_at => shopify_order.updated_at,
:status => Order.get_status(shopify_order),
:payment_status => shopify_order.financial_status,
:fulfillment_status => Order.get_fulfillment_status(shopify_order),
:payment_method => shopify_order.processing_method,
:gateway => shopify_order.gateway,
:currency => shopify_order.currency,
:subtotal_price => shopify_order.subtotal_price,
:subtotal_tax => shopify_order.total_tax,
:total_discounts => shopify_order.total_discounts,
:total_line_items_price => shopify_order.total_line_items_price,
:total_price => shopify_order.total_price,
:total_tax => shopify_order.total_tax,
:total_weight => shopify_order.total_weight,
:taxes_included => shopify_order.taxes_included,
:shop_id => shop.id,
:email => shopify_order.email,
:order_note => shopify_order.note
)
end
如您所见,我们正在遍历每个订单,确定它是否存在(然后加载现有订单或创建新订单),然后调用 update_attributes 传递订单的详细信息。之后,我们创建或更新每个关联。每个关联模型看起来都与此非常相似:
class << self
def add_details(order, tax_lines)
tax_lines.each do |shopify_tax_line|
taxline = Taxline.find_or_create_by_order_id(:order_id => order.id)
taxline.update_details(shopify_tax_line)
end
end
end
def update_details(tax_line)
self.update_attributes(:price => tax_line.price, :rate => tax_line.rate, :title => tax_line.title)
end
我已经研究了 activerecord-import gem,但不幸的是,它似乎更适合批量创建记录,而不是按照我们的要求进行更新。
提高性能的最佳方法是什么?
非常感谢。
更新:
我提出了这个微小的改进,它基本上消除了更新新创建的订单的调用(每个订单少一个查询)。
def add_details(shop, shopify_orders)
shopify_orders.each do |shopify_order|
values = {:order_id => shopify_order.id.to_s, :shop_id => shop.id,
:order_name => shopify_order.name,
:order_created_at => shopify_order.created_at,
:order_updated_at => shopify_order.updated_at,
:status => Order.get_status(shopify_order),
:payment_status => shopify_order.financial_status,
:fulfillment_status => Order.get_fulfillment_status(shopify_order),
:payment_method => shopify_order.processing_method,
:gateway => shopify_order.gateway,
:currency => shopify_order.currency,
:subtotal_price => shopify_order.subtotal_price,
:subtotal_tax => shopify_order.total_tax,
:total_discounts => shopify_order.total_discounts,
:total_line_items_price => shopify_order.total_line_items_price,
:total_price => shopify_order.total_price,
:total_tax => shopify_order.total_tax,
:total_weight => shopify_order.total_weight,
:taxes_included => shopify_order.taxes_included,
:email => shopify_order.email,
:order_note => shopify_order.note}
get_order = Order.where(:order_id => shopify_order.id.to_s, :shop_id => shop.id)
if get_order.blank?
order = Order.create(values)
else
order = get_order.first
order.update_attributes(values)
end
ShippingLine.add_details(order, shopify_order.shipping_lines)
LineItem.add_details(order, shopify_order.line_items)
Taxline.add_details(order, shopify_order.tax_lines)
Fulfillment.add_details(order, shopify_order.fulfillments)
Note.add_details(order, shopify_order.note_attributes)
Discount.add_details(order, shopify_order.discount_codes)
billing_address = shopify_order.billing_address rescue nil
if !billing_address.blank?
BillingAddress.add_details(order, billing_address)
end
shipping_address = shopify_order.shipping_address rescue nil
if !shipping_address.blank?
ShippingAddress.add_details(order, shipping_address)
end
payment_details = shopify_order.payment_details rescue nil
if !payment_details.blank?
PaymentDetail.add_details(order, payment_details)
end
end
end
对于关联的对象:
class << self
def add_details(order, tax_lines)
tax_lines.each do |shopify_tax_line|
values = {:order_id => order.id,
:price => tax_line.price,
:rate => tax_line.rate,
:title => tax_line.title}
get_taxline = Taxline.where(:order_id => order.id)
if get_taxline.blank?
taxline = Taxline.create(values)
else
taxline = get_taxline.first
taxline.update_attributes(values)
end
end
end
end
有更好的建议吗?
【问题讨论】:
-
您处理大量数据,这很正常,速度很慢。这段代码的上下文是什么?它在 api 中吗?它在网络请求中吗?它是阻塞代码吗?您是否需要非常快速地提供数据,或者您可以将一些作业推送到队列中而忘记它?
-
Hej @Intrepidd。我们正在使用一个 API,然后在后台作业中运行它,而前端通过 AJAX 调用检查它何时完成。所以基本上,首先我们通过 API 获取订单,然后遍历它们以放置在 DB 中。当用户第一次安装我们的应用程序时,我们进行了大部分的导入,然后用户等待工作完成。之后,更重要的是保持更新并获取任何新订单。任何有关使其更快的帮助表示赞赏。
-
为什么不使用 get_order = Order.find_or_create_by 而不是条件句?
-
@Fenec 好点。我实际上正在考虑改用
get_order = Order.where(:order_id => shopify_order.id.to_s, :shop_id => shop.id).first_or_create。它会使代码更漂亮,但不确定它是否会加快速度。更新了上面的代码示例以反映这一点。 -
你试过update_all:apidock.com/rails/ActiveRecord/Base/update_all/class
标签: ruby-on-rails ruby performance postgresql updates