Массовая вставка записей в таблицу Active Record

Я обнаружил, что мой Model.create! заявления занимали очень много времени, когда я добавлял большое количество записей одновременно. Посмотрел на ActiveRecord-Import, но он не работал с массивом хешей (это то, что у меня есть и которое, как мне кажется, довольно распространено). Как я могу улучшить производительность?

Используйте жмем activetecord-import . Скажем, вы читаете 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 

Благодаря Chris Heald @cheald за его статью за 2009 год, я показал, что лучший способ – это команда вставки нескольких строк.

Добавил следующий код в файл initializers/active_record.rb , изменил мои Model.create!(...) на Model.import!(...) и ушел. Пара предупреждений:

1) Он не проверяет данные.
2) Он использует форму команды SQL INSERT, которая читается как …

 INSERT INTO  (field-1, field-2, ...) VALUES (value-1-1, value-1-2, ...), (value-2-1, value-2-2, ...), ...`

… который не может быть правильным синтаксисом для всех баз данных, но он работает с Postgres. Нетрудно изменить код для соответствующего синтаксиса для вашей версии SQL.

В моем конкретном случае вставка записей 19K + в простую таблицу на моей машине разработки (MacBook Pro с 8 ГБ оперативной памяти, 2,4 ГГц Intel Core i5 и SSD) пошла с 223 секунд, используя «model.create!» до 7.2 секунд, используя «model.import!».

 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 

У меня возникли проблемы с большим количеством записей (> 10000), поэтому я изменил код для работы в группах по 1000 записей за раз. Вот ссылка на новый код:

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

Вы также можете использовать gem activerecord-insert_many . Просто создайте массив объектов!

 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 
  • Вложенные rails content_tag
  • raw vs. html_safe против h для unescape html
  • link_to и remote => true + jquery: Как? Помогите?
  • Где хранить конфиденциальные данные в приложении public rails?
  • Capybara с субдоменами - default_host
  • Ruby on Rails will_paginate массив
  • Новые данные, не сохраняющиеся в столбце массива Rails в Postgres
  • Rails 4 вложенных атрибутов и has_many: через associaton в форме
  • rails media file stream принимает запрос диапазона байтов через send_data или метод send_file
  • Как проверить ответ JSON с помощью RSpec?
  • Установка солнечного пятна solr с рельсами в производственной среде
  • Давайте будем гением компьютера.