0

10分ごとに約5000件のバックグラウンドジョブを実行しています。各ジョブは外部APIに対して要求を行い、データベース内の新規レコードを追加したり、既存のレコードを更新したりします。各API要求は約100個のアイテムを返します。したがって、10分ごとに50,000個のCREATEまたはUPDATE SQLクエリを作成しています。多くのレコードを一度にRailsで更新する

私がこれを処理する方法は、返される各APIアイテムに固有のIDがあります。このIDを持つ投稿をデータベースから検索し、存在する場合はモデルを更新します。それが存在しない場合は、新しいものを作成します。

APIレスポンスを想像してみては次のようになります。

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 

これらの各:私は私の親モデルでこのコードを実行後、変数collection

に設定されている

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

個々の通話は非常に速いですが、私が短期間に5万人をやっていると、実際にはそれが増え、減速する可能性があります。

私はこれを処理することができ、より効率的な方法があるのならば、私は次のように代わりに何かを考えていた思ったんだけど:

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_columnsを使用してください。callbacks/validations DBクエリーのトレードオフが良いと思うより多くのルビコード操作だけです。

+0

ああ大丈夫です。一度にすべての作成を行うというアイデアは間違いありません。私が問題であることを見いだすことができるのは、 'ChildModel.where(external_id:collection2.keys)'、 'collection2.keys'は潜在的に最大100個の鍵になる可能性があります。配列の多くのキー? – goddamnyouryan

+1

私はそれが問題でなければならないとは思わないが、 'where'クエリで列の1000を取り出した。列が索引付けされていないかどうかは重要であろう。もし' external_id'を索引すると良いだろう。 –

関連する問題