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',那會更好。 –