在Rails中一次更新大量记录

时间:2017-05-28 17:06:18

标签: ruby-on-rails performance postgresql optimization query-optimization

我有一份背景工作,我每10分钟运行一次大约5,000个。每个作业向外部API发出请求,然后在我的数据库中添加新记录或更新现有记录。每个API请求返回大约100个项目,因此每10分钟我创建50,000个CREATE或UPDATE sql查询。

我现在处理这个问题的方法是,返回的每个API项都有一个唯一的ID。我在我的数据库中搜索具有此id的帖子,如果它存在,则更新模型。如果它不存在,则会创建一个新的。

想象一下api响应如下:

[
  {
    external_id: '123',
    text: 'blah blah',
    count: 450
  },
  {
    external_id: 'abc',
    text: 'something else',
    count: 393
  }
]

设置为变量collection

然后我在我的父模型中运行此代码:

class ParentModel < ApplicationRecord
  def update
    collection.each do |attrs|
      child = ChildModel.find_or_initialize_by(external_id: attrs[:external_id], parent_model_id: self.id)
      child.assign_attributes attrs
      child.save if child.changed?
    end
  end
end

这些个别电话中的每一个电话都非常快,但是当我在短时间内完成50,000次电话时,它确实会加起来,并且会减慢速度。

我想知道是否有一种更有效的方法可以解决这个问题,我想做的事情就像:

class ParentModel < ApplicationRecord
  def update
    eager_loaded_children = ChildModel.where(parent_model_id: self.id).limit(100)
    collection.each do |attrs|
      cached_child = eager_loaded_children.select {|child| child.external_id == attrs[:external_id] }.first
      if cached_child
        cached_child.update_attributes attrs
      else
        ChildModel.create attrs
      end
    end
  end
end

基本上我会保存查找,而是预先做一个更大的查询(这也很快)但在内存中进行权衡。但这似乎不会是那么多时间,可能会稍微加快查找部分,但我仍然需要进行100次更新并创建。

我是否有某种方法可以进行批量更新,而我没有想到?还有什么明显可以让它更快,或减少我正在做的查询量?

1 个答案:

答案 0 :(得分:1)

您可以这样做:

collection2 = collection.map { |c| [c[:external_id], c.except(:external_id)]}.to_h

def update
  ChildModel.where(external_id: collection2.keys).each |cm| do
    ext_id = cm.external_id
    cm.assign_attributes collection2[ext_id]
    cm.save if cm.changed?
    collection2.delete(ext_id)
  end
  if collection2.present?
    new_ids = collection2.keys
    new = collection.select { |c| new_ids.include? c[:external_id] }
    ChildModel.create(new)
  end
end

更好,因为

  • 一次性获取所有必需的记录
  • 一次创建所有新记录

如果您不需要update_columns / callbacks,则可以使用validations 唯一的缺点,更多的ruby代码操作,我认为这是数据库查询的一个很好的权衡..