在Rails种子文件中快速导入大量数据的方法

你有没有遇到过想要在Seed文件中编写代码一次性输入大量数据的时候呢?我想要将大量素数输入到数据库中,于是考虑了一种快速的输入方法。

环境:Ruby 2.1,Rails 4.0.2
电脑规格:
MacBookAir 13英寸,2011年中期
处理器 1.7 GHz Intel Core i5
内存 4 GB 1333 MHz DDR3

数据库:mysql2

这张桌子非常简单。
以下为迁移记录。
只有两个列,分别是ID和质数两个。


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。

种子文件如下所示

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

一百七十二秒(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语句来测试一下。

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。

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秒!!!
答案是13秒!!!
成绩是13秒!!!
报告是13秒!!!

时间减少了19秒。

从最初的172秒到13秒,速度提升了约13倍。

如果批量插入将变快,我是否可以试试?我得到了这样的评论并尝试了一下。
参考文章(http://qiita.com/xend/items/79184ded56158ea1b97a)。
据说,如果在gem中安装activerecord-import,就可以在import方法中使用。

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直接编写可能会更快,但效果不太好,我正在考虑中。

还在寻找能更快的方式。

如果有更快的方法,请留下评论!

bannerAds