2

我有 EventsController 创建如下所示的操作:

class EventsController < ApplicationController

  def create
    @event = Event.new(params[:event].slice(*Event.accessible_attributes))
    if @event.save
      DraftBuilder.new(event: @event).build(params[:event][:file].path) 
      redirect_to @event
    else
      render :new
    end
  end

end

params[:event][:file]是一个文件,用户可以通过 file_field_tag 通过 Event#new 操作提交。

DraftBuilder#build方法除其他外,解析给定文件并在数据库中创建大约 1000 条记录(跨多个表将数据保存到数据库中)。

我遇到的问题是 DraftBuilder#build 真的很慢。它很慢,因为我在循环中保存记录,而 Active Record 为每次保存创建新事务。

简化的 DraftBuilder#build 可能如下所示:

class DraftBuilder
  def build(file)
    @data = parse(file) #@data is an array of hashes with 1000+ records
    @data.each do |pick_arguments|
      Pick.create(pick_arguments)
    end
  end
end

我找到了解决这个问题的方法。将控制器创建操作包装到 ActiveRecord::Base.transaction:

class EventsController < ApplicationController
  around_filter :transactions_filter, only: [:create]

    def transactions_filter
      ActiveRecord::Base.transaction do
        yield
      end
    end
end        

在这个解决方案有效的同时,它只创建一个事务,并将整个过程加快了大约 60 倍。这是解决这个问题的好方法吗?交易肯定不是为此设计的吗?从具有超过一千个条目的文件创建记录的其他选项是什么?

4

2 回答 2

3

减缓进程运行速度的最佳解决方案是使用后台作业,如delay_jobresquesidekiq

于 2013-02-28T23:32:06.630 回答
2

你有两种方法:

代替

@data.each do |pick_arguments|
  Pick.create(pick_arguments)
end
  1. 交易
     ActiveRecord::Base.transaction do
       @data.each do |pick_arguments|
         Pick.create(pick_arguments)
       end
     end  
    
  2. Gem activerecord-导入
    data = []
    @data.each do |pick_arguments|
      data << Pick.new(pick_arguments)
    end
    Pick.import data
    
于 2013-02-28T23:33:39.103 回答