私のModel.create!
ステートメントを大量のレコードを一度に追加すると、ステートメントの実行に非常に長い時間がかかりました。 ActiveRecord-Import を見てみましたが、ハッシュの配列では機能しませんでした(これは私が持っているもので、かなり一般的だと思います)。パフォーマンスを向上させるにはどうすればよいですか?
大量のレコード(> 10000)で問題が発生し始めたため、一度に1000レコードのグループで機能するようにコードを変更しました。新しいコードへのリンクは次のとおりです。
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
2009年のChris Heald @chealdに感謝 記事 .
次のコードを_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バージョンに適した構文のコードを変更することは難しくありません。
私の特定のケースでは、開発マシン(8GB RAM、2.4GHz Intel Core i5およびSSDを搭載したMacBook Pro)の単純なテーブルに19K +レコードを挿入すると、「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
_
activerecord-insert_many gemを使用することもできます。オブジェクトの配列を作ってください!
events = [{name: "Movie Night", time: "10:00"}, {name: "Tutoring", time: "7:00"}, ...]
Event.insert_many(events)
トランザクションを使用すると、一括挿入が大幅に高速化されます。
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