0

我有一份背景作业,每10分钟运行一次约5000个记录。每个作业都向外部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

你可以做这样的事情:

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_columnscallbacks/validations 唯一的缺点,更多的红宝石代码操作,我认为是一个很好的折衷分贝查询..

+0

啊,好吧,这是有道理的。我绝对喜欢一次完成所有创作的想法。我唯一可以预见的问题是'ChildModel.where(external_id:collection2.keys)','collection2.keys'可能长达100个密钥,对于这种情况来说,这不是一个非常有效的查询数组中的许多键? – goddamnyouryan

+1

我不认为这应该是一个问题,我已经用'where'查询获取了1000列。但是,如果列没有被索引,那么这很重要。如果你索引'external_id',那会更好。 –