概要
原著者の許諾を得て翻訳・公開いたします。
- 英語記事: Using pluck can save a bunch of memory - Andy Croll
- 原文公開日: 2018/09/16
- 著者: Andy Croll
Rails: pluckでメモリを大幅に節約する(翻訳)
Active Recordのモデルは信じられないほど柔軟性が高く、機能が山ほど搭載されています。メソッドのAPIが大量にあるため、Active Recordの個別のオブジェクトはメモリに読み込まれると非常に場所を取ります。
Active Recordのモデルにフィールドがたった1つあるだけでも(たとえばid
だけでも)、Railsが背後に控えているのです。
次のように書くのではなく
多数のオブジェクトをイテレーションしてメモリに全部読み込む。
Book.paperbacks.map { |book| book.title }
#=> ["Eloquent Ruby", "Sapiens", "Agile Web Development With Rails"]
Book.paperbacks.map(&:title)
#=> ["Eloquent Ruby", "Sapiens", "Agile Web Development With Rails"]
次のように書く
ActiveRelation#pluck
メソッドを用いて、必要なフィールドをデータベースから直接読み込む。
Book.paperbacks.pluck(:title)
#=> ["Eloquent Ruby", "Sapiens", "Agile Web Development With Rails"]
そうする理由
この方法は、スピードとメモリ使用量の効率に関連します。
Active Recordモデルが大量のメモリを食う問題は、特に巨大なコレクションを操作するときにつらくなります。最初の例では、データベースから読み込まれた行をひとつひとつオブジェクトに変換しているにもかかわらず、たった1つのフィールドしか使っていません。Railsがメモリ上に用意する、モデルのその他の機能は必要ありません。
必要なフィールドだけを読み込むことで、アプリが高速化し、メモリ使用量も削減されます。
そうしない理由があるとすれば
#pluck
メソッドがリクエストに応じて返す値は、Rubyの素の配列のみです。モデルのメソッドは一切読み込まれません。
Active Recordの実際のオブジェクトがどうしても必要な場合や、#pluck
の後でモデルを更新する必要がある場合は、残念ながら使えません。
素の配列の代わりに、スコープに続けてselect
することでActive Recordの完全なモデルを使うことを検討してもよいのですが、リクエストするフィールドしか含まれません。#pluck
ほどメモリ効率のよい方法は他にありません。
Book.paperbacks.select(:title).map { |book| book.title }
#=> ["Eloquent Ruby", "Sapiens", "Agile Web Development With Rails"]