4

Rails 3 を PostgreSQL で実行すると、

何百万もの小さなレコードを更新して移行しました。

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はまだ急落しています...

たぶん、タップを使用した私のソリューションは満足のいくものではありませんか? まだ大量選択ですか?find_each の後に一括選択を回避するにはどうすればよいですか?

ありがとう!

4

2 に答える 2

2

コードをデータベーストランザクションでラップすることも、何ActiveRecord::Migrationもしません。find_eachr.save!、保存のカスケード効果をカバーする個別のトランザクションにラップされます。

上記のコメントのように、update_allまたはrawexecuteを使用すると、一括更新の方が高速になります。それがあなたのしていることにふさわしいかどうかを知る方法はありません。find_eachまた、メモリの問題が発生している場合は、バッチサイズを微調整して、効果があるかどうかを確認できるはずです。そうでない場合は、それらのオブジェクトをどこかで保持している可能性があります。

于 2011-05-03T16:13:11.297 に答える
0

おそらく、find_each の戻り値を返すのではなく、戻り値として機能する最後のステートメントを追加するメソッドを構築できます。最後の「終わり」を次のように置き換えることができます

end ; nil
于 2011-11-29T22:35:59.823 に答える