简体   繁体   English

Rails构建并大量保存

[英]Rails build and save in bulk

I've got something like the following: 我有类似以下内容:

module Bar < ActiveRecord::Base
  belongs_to :foo
  ...

module Foo < ActiveRecord::Base
  has_many :bars, dependent: :destroy

  def build_bars
    1000.times do |i|
      bars.build(num: i)
    end
  end

  def create_default_bars!
    build_bars
    save
  end

Notice that Foo#build_bars is cheap. 请注意, Foo#build_bars很便宜。 Even though it loops 1000 times, it takes very little time. 即使它循环1000次,也只需要很少的时间。 But then, once you hit save , suddenly ActiveRecord decides to perform 1000 inserts, which is incredibly slow. 但是,一旦你点击save ,突然ActiveRecord决定执行1000次插入,这非常慢。

How can I write a custom save_the_bars method such that it performs a single bulk INSERT query for all of the bars I have built up (I remind you, the 1000 build s are apparently very cheap), but which functions as a drop-in replacement for save in this example? 如何编写自定义save_the_bars方法,以便它为我构建的所有bars执行单个批量INSERT查询(我提醒您,1000 build显然非常便宜),但它可以作为替代品在这个例子中save


I'm expecting an answer along the lines of recommendation #3 of this blog post: 我希望能够按照这篇博客文章的第3号推荐回答:

https://www.coffeepowered.net/2009/01/23/mass-inserting-data-in-rails-without-killing-your-performance/ https://www.coffeepowered.net/2009/01/23/mass-inserting-data-in-rails-without-killing-your-performance/

But since my example uses build and relies on some slightly nontrivial rails magic, it isn't immediately obvious how to translate it over. 但是因为我的例子使用了build并且依赖于一些稍微不重要的rails魔法,所以如何翻译它并不是很明显。 Bonus points for benchmarks! 基准的奖励积分!

I would try the activerecord-import gem. 我会尝试activerecord-import gem。

def save_the_bars(bars)
  Bars.import bars
end

This call to import does whatever is most efficient for the underlying database adapter. 这种对导入的调用会对底层数据库适配器执行最有效的操作。 Pretty slick, eh? 漂亮,嗯?

For bonus points: Benchmarks 奖励积分: 基准


Question-asker here, hijacking this answer with details on what I did following the above suggestion: 提问者在这里,劫持了这个答案,详细说明了我按照上述建议做了什么:

def build_bars
  built_bars = []
  1000.times do |i|
    built_bars << bars.build(num: i)
  end
  built_bars
end

def create_default_bars
  save
  Bar.insert built_bars, validate: false
  reload
end

This gave a nice speedup for relatively little effort. 这为相对较少的努力提供了很好的加速。 I still suspect that more speedup could be had (by leveraging the nuances of insert ) but I'm satisfied with this for now. 我仍然怀疑可以获得更多的加速(通过利用insert的细微差别),但我现在对此感到满意。 In my use case, it was safe to turn off validation, since the method generating all the Bar s is guaranteed to generate valid ones. 在我的用例中,关闭验证是安全的,因为生成所有Bar的方法保证生成有效的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM