我发现当我一次添加大量记录时,我的Model.create!
语句需要很长时间才能运行。看看ActiveRecord-Import,但它不适用于哈希数组(这是我所拥有的,我认为这很常见)。如何提高性能?
答案 0 :(得分:22)
使用activerecord-import gem。我们假设您正在读取CSV文件并生成Product
目录,并且您希望以1000个批次插入记录:
batch,batch_size = [], 1_000
CSV.foreach("/data/new_products.csv", :headers => true) do |row|
batch << Product.new(row)
if batch.size >= batch_size
Product.import batch
batch = []
end
end
Product.import batch
答案 1 :(得分:10)
感谢Chris Heald @cheald的2009 article,并向我展示了最好的方法是多行插入命令。
在我的initializers/active_record.rb
文件中添加了以下代码,将我的Model.create!(...)
电话更改为Model.import!(...)
,然后将其删除。一些警告:
1)它不验证数据 2)它使用SQL INSERT命令的形式,其内容类似于......
INSERT INTO <table> (field-1, field-2, ...)
VALUES (value-1-1, value-1-2, ...), (value-2-1, value-2-2, ...), ...`
...这可能不是所有数据库的正确语法,但它适用于Postgres。为SQL版本更改适当语法的代码并不困难。
在我的特殊情况下,将19K +记录插入我的开发机器(带有8GB RAM,2.4GHz Intel Core i5和SSD的MacBook Pro)上的简单表中,使用'model.create!'从223秒开始。使用'model.import!'到7.2秒。
class ActiveRecord::Base
def self.import!(record_list)
raise ArgumentError "record_list not an Array of Hashes" unless record_list.is_a?(Array) && record_list.all? {|rec| rec.is_a? Hash }
key_list, value_list = convert_record_list(record_list)
sql = "INSERT INTO #{self.table_name} (#{key_list.join(", ")}) VALUES #{value_list.map {|rec| "(#{rec.join(", ")})" }.join(" ,")}"
self.connection.insert_sql(sql)
end
def self.convert_record_list(record_list)
key_list = record_list.map(&:keys).flatten.uniq.sort
value_list = record_list.map do |rec|
list = []
key_list.each {|key| list << ActiveRecord::Base.connection.quote(rec[key]) }
list
end
return [key_list, value_list]
end
end
答案 2 :(得分:5)
我开始遇到大量记录(&gt; 10000)的问题,因此我修改了代码,以便一次以1000个记录为一组进行操作。以下是新代码的链接:
答案 3 :(得分:1)
您还可以使用activerecord-insert_many gem。只需制作一个对象数组!
events = [{name: "Movie Night, time: "10:00"}, {name: "Tutoring", time: "7:00"}, ...]
Event.insert_many(events)
答案 4 :(得分:0)
使用交易加速批量插入很多!
Model.transaction do
many.times{ Model.create! }
end
如果涉及多个模型,请对受影响的每个模型执行Model.transaction:
Model1.transaction do
Model2.transaction do
many.times do
m1 = Model1.create!
m1.add_model2
end
end
end
答案 5 :(得分:0)
对于 Rails 6.x,使用 insert_all。