长期运行到死亡的迁移/ find_each

时间:2011-05-03 11:05:03

标签: ruby-on-rails activerecord rails-migrations

使用PostgreSQL运行Rails 3,

我进行了迁移,更新了数百万条小记录。

Record.find_each do |r|
  r.doing_incredibly_complex_stuff
  r.save!
  puts "#{r.id} updated"
end

由于我认为ActiveRecord在事务中包含了这样的更新,因此“提交”时间非常长,所占用的内存很大,而上述示例中的每条记录都已“打印”在屏幕上。

那么,我可以在事务外运行这个find_each - 虽然它非常安全 - 节省了大量的“提交”时间和内存吗?

一种 ActiveRecord :: Base.without_transaction do ...;结束我想: - )

或者: 我错了,迁移不包含在事务中,我看到的时间只是应用SQL更新语句吗?

编辑:似乎没有与事务的链接,这是我在中断迁移后得到的堆栈跟踪,当所有内容都已打印在屏幕上并且RAM从500MB可用空间减少到~30MB时:

IRB::Abort: abort then interrupt!!
from /Users/clement/.rvm/gems/ruby-1.9.2-p136@gemset/gems/activesupport-3.0.4/lib/active_support/whiny_nil.rb:46:in `call'
from /Users/clement/.rvm/gems/ruby-1.9.2-p136@gemset/gems/activesupport-3.0.4/lib/active_support/whiny_nil.rb:46:in `method_missing'
from /Users/clement/.rvm/gems/ruby-1.9.2-p136@gemset/gems/activerecord-3.0.4/lib/active_record/connection_adapters/postgresql_adapter.rb:978:in `flatten'
from /Users/clement/.rvm/gems/ruby-1.9.2-p136@gemset/gems/activerecord-3.0.4/lib/active_record/connection_adapters/postgresql_adapter.rb:978:in `block in select'
from /Users/clement/.rvm/gems/ruby-1.9.2-p136@gemset/gems/activerecord-3.0.4/lib/active_record/connection_adapters/postgresql_adapter.rb:977:in `map'
from /Users/clement/.rvm/gems/ruby-1.9.2-p136@gemset/gems/activerecord-3.0.4/lib/active_record/connection_adapters/postgresql_adapter.rb:977:in `select'
from /Users/clement/.rvm/gems/ruby-1.9.2-p136@gemset/gems/activerecord-3.0.4/lib/active_record/connection_adapters/abstract/database_statements.rb:7:in `select_all'
from /Users/clement/.rvm/gems/ruby-1.9.2-p136@gemset/gems/activerecord-3.0.4/lib/active_record/connection_adapters/abstract/query_cache.rb:56:in `select_all'
from /Users/clement/.rvm/gems/ruby-1.9.2-p136@gemset/gems/activerecord-3.0.4/lib/active_record/base.rb:467:in `find_by_sql'
from /Users/clement/.rvm/gems/ruby-1.9.2-p136@gemset/gems/activerecord-3.0.4/lib/active_record/relation.rb:64:in `to_a'
from /Users/clement/.rvm/gems/ruby-1.9.2-p136@gemset/gems/activerecord-3.0.4/lib/active_record/relation.rb:356:in `inspect'
from /Users/clement/.rvm/gems/ruby-1.9.2-p136@gemset/gems/railties-3.0.4/lib/rails/commands/console.rb:44:in `start'
from /Users/clement/.rvm/gems/ruby-1.9.2-p136@gemset/gems/railties-3.0.4/lib/rails/commands/console.rb:8:in `start'
from /Users/clement/.rvm/gems/ruby-1.9.2-p136@gemset/gems/railties-3.0.4/lib/rails/commands.rb:23:in `<top (required)>'
from script/rails:6:in `require'
from script/rails:6:in `<main>'
编辑(2):哇。事实证明它很长,因为find_each在迭代后返回所有元素。

我试过了:

Record.tap do |record_class|
  record_class.find_each do |r|
    r.doing_incredibly_complex_stuff
    r.save!
    puts "#{r.id} updated"
  end
end
=> Record(id: integer, ...)

所以它按预期立即取回了控制台。 :)

但后来我仍然看到一种奇怪的行为:RAM不会释放。相反,一旦我退出学期,RAM仍然会下降......

也许我的tap解决方案不满意?它还在大规模选择吗?如何在find_each之后避免质量选择?

谢谢!

2 个答案:

答案 0 :(得分:2)

ActiveRecord::Migrationfind_each都没有做任何事情来将您的代码包装在数据库事务中。 r.save!将包含在单个事务中,该事务涵盖了保存的任何级联效果。

与上述评论一样,使用update_all或原始execute对于批量更新会更快。我无法判断这是否适合您正在做的事情。此外,如果您遇到内存问题,您应该可以在find_each上调整批量大小,看看它是否有效。如果没有,你可能会在某处抓住这些物体。

答案 1 :(得分:0)

也许您可以构造方法来添加最后一个语句作为返回值,而不是返回find_each的返回值。您可以用

替换最后一个“结束”
end ; nil