我通常需要执行这样的迁移:
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也没有响应。
为什么?可以采取哪些措施来避免这种迁移停机?有没有更好的方法来添加迁移和更新现有记录?
由于
答案 0 :(得分:1)
如果添加具有默认值的新列,则无需循环现有记录来添加它。数据库引擎应该处理这个问题。
如果迁移在一个事务中,并且在事务期间其他地方也修改了被修改的数据,那么PostgreSQL可能会挂起。答案 1 :(得分:1)
@MartinSamson是对的,默认应该处理这种情况。如果由于某种原因你需要对默认无法处理的值进行其他类型的更新,那么在sql中执行它会更有效。例如。
execute("update members set new_field='value'")
而不是以这种方式执行所有实体。
哦,另外,我强烈建议您不要在迁移中引用您的对象,这只会导致问题。例如,如果您以后从应用程序中删除Member
类,该怎么办?然后,您的迁移将在下次从头开始运行时失败,这很烦人。在SQL中做这件事要好得多。
答案 2 :(得分:0)
答案 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
。在事务中包装循环也可以获得更好的结果。