19

Model.create!一度に多数のレコードを追加すると、ステートメントの実行に非常に長い時間がかかることがわかりました。ActiveRecord-Importを調べましたが、ハッシュの配列では機能しませんでした(これは私が持っているものであり、かなり一般的だと思います)。どうすればパフォーマンスを向上させることができますか?

4

6 に答える 6

23

activerecord-importgemを使用します。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
于 2013-03-10T02:21:44.090 に答える
12

2009年の記事を提供してくれたChrisHeald@chealdに感謝します。これは、複数行の挿入コマンドが最善の方法であることを示してくれました。

initializers/active_record.rb次のコードをファイルに追加し、Model.create!(...)呼び出しを変更しましModel.import!(...)た。いくつかの注意点:

1)データを検証しません。
2)次のようなSQLINSERTコマンドの形式を使用します...

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を搭載したMacBookPro)の単純なテーブルに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
于 2013-03-10T01:17:10.810 に答える
5

多数のレコード(> 10000)で問題が発生し始めたため、一度に1000レコードのグループで機能するようにコードを変更しました。新しいコードへのリンクは次のとおりです。

https://gist.github.com/jackrg/76ade1724bd816292e4e

于 2014-05-16T18:16:19.237 に答える
1

activerecord-insert_manygemを使用することもできます。オブジェクトの配列を作成するだけです!

events = [{name: "Movie Night", time: "10:00"}, {name: "Tutoring", time: "7:00"}, ...]

Event.insert_many(events)
于 2015-12-18T15:46:05.607 に答える
1

Rails 6.xの場合は、insert_allを使用します。

于 2021-02-22T23:11:48.630 に答える
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
于 2016-02-11T17:17:12.660 に答える