もふもふ技術部

Ruby on Rails 4、N+1問題にメソッドのメモ化で対策する


そんなこと知っているとは思いますが、メモ化とはプログラム最適化をテクニックの一つで、同じ結果になるだろうメソッドの実行結果を保持しておいて、再実行を防ぐ仕組みです。詳しいアルゴリズムについては省略しますが、キャッシュに近いイメージですね。

N+1問題は、Ruby on Railsでしばしば発生する問題なのですが、一覧画面で複数モデルを参照するデータを表示するとき(通常テーブルとマスタテーブルみたいな)、一行表示するごとに、別テーブルにクエリを投げてしまうことで大量のクエリが発生してパフォーマンスが落ちる問題です。

通常はActiveRecordで検索する際にincludeオプションなどを指定してあらかじめロードしておけば対策出来るのですが、それが出来ない状態(今回はmondoidとactiverecordのリレーションのためincludeできなかった)にぶちあたってしまったので他の解決法を探してたんですが、メモ化で回避出来ないかと考えたわけです。

サンプルデータ

こんな感じの部署(departments)テーブルがある。codeは分類コードみたいなイメージ。

分類コード(code)ごとに部署名一覧を表示する

department.rb

view

ログ。
codeは0001,0002の二種類あるのでdepartmentsを参照するクエリが2回実行されてしまう。2件ならたいした問題ではないけど、100件のcodeがあったらかなり遅くなると思う。

memoistでメモ化する

ではメモ化してみます。memoistというgemを使うと非常に簡単。

code_name_mapメソッドでcodeに対するdepartmentをハッシュにしてメモ化します。引数が同じuser_idなら、code_name_mapは再計算されません。

after_commitで self.class.flush_cache を実行しているのは、部署が更新・削除された際にメモ化されているキャッシュを削除するためです。

viewはそのままでOK。

ログ。このように一回しか実行されませんね!

もっと他にもN+1問題の対策はありそうですが、ひとまず意図したことは出来ました。

The following two tabs change content below.
原田 敦

原田 敦

日本CAWのエンジニア。もふもふ部の部長。得意分野はRuby on Railsを使った小規模WEBアプリケーションを高速で開発すること。週末の楽しみは一人お菓子パーティー。三度の飯より小動物をもふもふするのが好きです。