LoginSignup
14

More than 5 years have passed since last update.

Rails seedファイルに大量のデータを早くいれる方法

Last updated at Posted at 2014-01-17

Seedファイルにコードを書いて一度にたくさんのデータを入れたい時ってありませんか?
自分は素数の数をデータベースにたくさん入れたく、スピード早くいれる方法を考えた。

環境: ruby2.1
rails4.0.2
パソコンのスペック:
  MacBookAir 13-inch, Mid 2011
  プロセッサ 1.7 GHz Intel Core i5
  メモリ 4 GB 1333 MHz DDR3

データベース: mysql2

テーブルは非常にシンプルなもの
下記マイグレーション
カラムは二つのみ
IDとprime_numberの二つ


class CreatePrimeNumbers < ActiveRecord::Migration
  def change
    create_table :prime_numbers do |t|
      t.integer :prime_number
    end
  end
end

コマンドは下記を何度も試して計測する。

> bundle exec time spring rake db:reset

極力メモリは消費しないようにする。ブラウザーも閉じて、必要ないアプリケーションも閉じる。
起動しているアプリはターミナルとエディタぐらいにする。

ネットで■ パフォーマンス比較 Cassandra、Mongodb、SQLite、H2、MySQL、Postgres の記事をみて最初はSQLite3を使おうと思った。

seedファイルは下記の通り

seed.rb
require 'Prime'
Prime.each(10 ** 6) do |prime|
  PrimeNumber.create(
  prime_number: prime
)
end

0から100万までの間に素数が何個あるか検査してデータベースに入れていく。

> bundle exec time spring rake db:reset
-- create_table("prime_numbers", {:force=>true})
   -> 0.0120s
-- initialize_schema_migrations_table()
   -> 0.0057s
      172.77 real         0.34 user         0.04 sys

172秒(sqlite3)だった。

mysqlで試してみた。

> bundle exec time spring rake db:reset
-- create_table("prime_numbers", {:force=>true})
   -> 0.0279s
-- initialize_schema_migrations_table()
   -> 0.0396s
       97.99 real         0.34 user    

97秒(mysql2)と1.7倍に縮む、、、、

ActiveRecordを使ってるから遅い。
だからSQLの直打ちで書いて試してみる。

seed.rb
require 'Prime'

Prime.each_with_index(10 ** 6) do |prime, index|
  ActiveRecord::Base.connection.execute("Insert INTO prime_numbers (id, prime_number) VALUES
  (  #{index + 1}, #{prime} );")
end
> bundle exec time spring rake db:reset
-- create_table("prime_numbers", {:force=>true})
   -> 0.0173s
-- initialize_schema_migrations_table()
   -> 0.0368s
       32.03 real         0.33 user         0.03 sys

結果は32秒(mysql2)と大幅にアップ!

ちなみにSQLite3だと88秒(sqlite3)これまた大幅にアップ。

並列処理したらもっと早くなるんじゃないかと思い試してみる。
Rubyで並列処理が簡単に導入できるgemのParallelを入れる。

seed.rb
require 'Prime'
require 'parallel'

primes = Prime.each_with_index(10 ** 6)

Parallel.each(primes, :in_processes => 10 ) do |prime, index|
  ActiveRecord::Base.connection.execute("Insert INTO prime_numbers (id, prime_number) VALUES
      (  #{index + 1}, #{prime} );")
end
> bundle exec time spring rake db:reset
-- create_table("prime_numbers", {:force=>true})
   -> 0.0241s
-- initialize_schema_migrations_table()
   -> 0.0410s
       13.96 real         0.33 user         0.03 sys

結果は13秒!!!

19秒も縮んだ。

当初の172秒から13秒までと約13倍も早くなった。

バルクインサートしたら早くなるんじゃないか??と言うコメントをいただいて試してみた。
参考記事(http://qiita.com/xend/items/79184ded56158ea1b97a)
activerecord-importをgemにインストールすればimportのメソッドで使えるようになるらしい。

seed.rb
require 'Prime'
arr_prime = []
Prime.each(10 ** 6) do |prime|
  arr_prime << PrimeNumber.new(:prime_number => prime)
end

PrimeNumber.import arr_prime
 bundle exec time spring rake db:reset
-- create_table("prime_numbers", {:force=>true})
   -> 0.0439s
-- initialize_schema_migrations_table()
   -> 0.0468s
       12.80 real         0.40 user         0.04 sys

結果は12秒!!!

SQLで直書きで書けばもっと早くなりそうだがあまりうまくいかず、思案中である。

まだまだもっと早くする方法があるのか探し中。

もっと早くする方法があればコメントお待ちしております!

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14