我有一份背景工作,我每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次更新并创建。
我是否有某种方法可以进行批量更新,而我没有想到?还有什么明显可以让它更快,或减少我正在做的查询量?
答案 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代码操作,我认为这是数据库查询的一个很好的权衡..