Rails 6のinsert_allで大量のダミーデータを短時間で作成する

Ruby

Railsアプリケーションでバルクインサート(複数のレコードを一括でインサートする方法)を実現する場合、Rails 6以前はactiverecord-importを利用する必要がありました。
Rails 6からはバルクインサートを実現するinsert_allというメソッドが標準で利用できます。

今回はinsert_allの利用方法について紹介します。

一般的なデータの作成方法

Userモデルに10,000件データを用意すると仮定します。
1件ずつデータを作成する場合は以下のようになります。

10_000.times do |n|
  User.create!(
    name: "user_#{n+1}"
  )
}

insert_allを利用したデータの作成方法

insert_allを利用して10,000件データを作成する場合は以下のようになります。
insert_allではcreated_atとupdated_atも明記する必要があります。

users = []
10_000.times do |n|
  time = Time.current
  users << { name: "user_#{n+1}", created_at: time, updated_at: time }
end
User.insert_all users

ベンチマークの比較

insert_allの効率を確認するため、1万件データを作成する場合のベンチマークを計測してみます。
ベンチマークテストはRuby標準のBenchmarkモジュールを利用します。

Benchmarkモジュールの利用方法についてはRuby標準のBenchmarkモジュールを利用したベンチマークの実行・確認方法で紹介をしています。

seeds.rb

Benchmark.bm(20) do |x|
  x.report("baseline") {
    10_000.times do |n|
      User.create!(name: "user_#{n+1}")
    end
  }
  x.report("insert_all") {
    users = []
    10_000.times do |n|
      users << { name: "user_#{n+1}", created_at: Time.current, updated_at: Time.current }
    end
    User.insert_all users
  }
end

結果は以下の通りです。insert_allのほうが処理時間が短いです。

$ rails db:seed
                           user     system      total        real
baseline              33.414657   4.211116  37.625773 ( 80.747791)
insert_all             1.139993   0.112310   1.252303 (  1.365058)

さいごに

Twitter(@nishina555)やってます。フォローしてもらえるとうれしいです!

参考