在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直接编写可能会更快,但效果不太好,我正在考虑中。
还在寻找能更快的方式。
如果有更快的方法,请留下评论!