为什么应用程序在rails迁移期间死亡?

时间:2012-02-02 03:17:58

标签: ruby-on-rails ruby-on-rails-3 postgresql activerecord

我通常需要执行这样的迁移:

class AddNewFieldToMember < ActiveRecord::Migration
  def self.up
    add_column :members, :new_field, :string, :default => 'instant'

    # Set existing records
    Member.reset_column_information
    Member.all.each do |member|
      member.new_field = 'instant'
      member.save
    end

  end

  def self.down
    remove_column :members, :new_field
  end
end

此特定成员表有大约10,000条记录。当我们像这样运行迁移时,一切都会消亡。迁移至少需要5分钟。 Rails没有响应,postgresql也没有响应。

为什么?可以采取哪些措施来避免这种迁移停机?有没有更好的方法来添加迁移和更新现有记录?

由于

4 个答案:

答案 0 :(得分:1)

如果添加具有默认值的新列,则无需循环现有记录来添加它。数据库引擎应该处理这个问题。

如果迁移在一个事务中,并且在事务期间其他地方也修改了被修改的数据,那么PostgreSQL可能会挂起。

答案 1 :(得分:1)

@MartinSamson是对的,默认应该处理这种情况。如果由于某种原因你需要对默认无法处理的值进行其他类型的更新,那么在sql中执行它会更有效。例如。

execute("update members set new_field='value'")

而不是以这种方式执行所有实体。

哦,另外,我强烈建议您不要在迁移中引用您的对象,这只会导致问题。例如,如果您以后从应用程序中删除Member类,该怎么办?然后,您的迁移将在下次从头开始运行时失败,这很烦人。在SQL中做这件事要好得多。

答案 2 :(得分:0)

马丁是对的,你可能根本不需要这样做,但是如果你想明确地设置值,或者需要这样做,否则......

如果您有10,000个成员,那么您将使用隐式事务发出10,000个更新语句。

查看update_all

答案 3 :(得分:0)

您应该避免访问迁移中的模型,除非它确实是不可避免的。

您的具体案例可以通过以下方式解决:

class AddNewFieldToMember < ActiveRecord::Migration
  def self.up
    add_column :members, :new_field, :string, :default => 'instant'

    # Set existing records
    execute <<-SQL
      update members set new_field = 'instant';
    SQL
  end

如果您真的需要在迁移中使用您的模型,至少使用最有效的方法find_each而不是all.each。在事务中包装循环也可以获得更好的结果。