更新日:
【Rails】 N+1問題とは?原因と対処法を徹底解説!
N+1問題とは、データベースからデータを取り出す際に、大量のSQLが実行されて動作が重くなるという問題です。
N+1問題と対処方法
この章では、N+1問題とその対処方法について解説します。
N+1問題の具体的な内容
railsではallメソッドやfindメソッドを使ってデータベースからデータを取得しています。
ですがターミナルのログを見ると実際には下のようにその都度SQLが実行されています。
例をあげて考えてみましょう。
ユーザーがツイートを投稿できるアプリがあったとします。
ユーザーの情報はusersテーブルに、ツイートの情報はtweetsテーブルに保存します。
トップページにはツイートの一覧を表示させるので、コントローラーでallメソッドを使い、tweetsテーブルからデータを取得します。
その際、上のように1回SQLが実行されます。
そして、トップページのビューでeachメソッドを使い、一つ一つのツイートを表示させるとします。
この時、usersテーブルとtweetsテーブルでアソシエーションを組んでいて、ツイートした人の名前もトップページで表示するようにします。
ツイートの情報全てはallメソッドで取得してきましたが、ツイートを投稿したユーザーの名前はusersテーブルから取得してこなければなりません。
ですのでeachメソッドで一つのツイートのユーザーの名前を表示させるたびにusersテーブルからレコードを取得するSQLが自動で実行されます。
もしツイートが5個あれば5回SQLが実行されます。
N個あればN回SQLが実行されるので、全てのレコードを取得する1回+N回SQLが実行されるのでN+1問題と呼ばれます。
1+N問題と言った方がわかりやすいかもしれません。
SQLが実行されるときには1回あたりわずかですが時間がかかります。
5回ぐらいの実行であれば動作にそれほど影響はないですが、これが100万回あったときはどうでしょうか?
読み込むまで相当な時間がかかってしまいますよね。
ですのでこの問題が起きないよう気をつける必要があります。
N+1問題の対処法
N+1問題に気付いたときには対処をする必要があります。
その際に使うのがincludesメソッドです。
includesメソッド
includesメソッドは、アソシエーションの関連付けを事前に取得してN +1問題を解決するメソッドです。
includesメソッドに渡す引数は、テーブル名ではなくアソシエーションで定義した関連名を指定します。
1
モデル名.includes(:関連名) # 関連名はテーブル名ではない
1
@tweets = Tweet.includes(:user)
上記のように、tweetsテーブルからデータを取得するときに、関連するusersテーブルのデータも取得してくれます。 eachメソッドで一つ一つ表示する際でもすでに表示させるデータを全て取得しているので、その都度SQLを実行する必要は無くなります。
includesメソッドについて詳しくは、N+1問題をincludesメソッドで解決しよう!を参考にしてください。
便利なGemを使ってみよう
複雑なアプリになってくると自分でN+1問題が発生しているか気づかない時があります。
そんなときに便利なGemがbullet
です。
bulletを使うとN+1問題が発生しているビューが表示されたときにポップアップで知らせてくれるので大変便利です。
bulletの使い方
それではbulletをアプリに導入する方法を解説します。
まずはGemfileのdevelopment環境にgemを追加します。
1
2
3
group :development do
gem 'bullet'
end
そしてターミナルでbundle install
をします。
次にconfig/environments/development.rb
に下記の記述をします。
1
2
3
4
5
6
7
8
9
10
AppName::Application.configure do
config.after_initialize do
Bullet.enable = true
Bullet.alert = true
Bullet.bullet_logger = true
Bullet.console = true
Bullet.rails_logger = true
Bullet.add_footer = true
end
end
これで準備完了です。
実際にn+1問題が発生しているビューを開くと下のようにポップアップウィンドウが開き、教えてくれます。
とても便利なgemなのでぜひインストールしておきましょう。
ターミナルのログでn+1問題を確認してみよう
それでは実際にどういうSLQが発行されているのか、ターミナルのログを見ると確認することができます。
まずはincludesメソッド
を使う前を見てみます。
このようにたくさんのSQLが実行されているがわかりますね。
次にincludesメソッド
を使った時を見てみましょう。
このように明らかにSQLが実行されている回数が違いますよね。N+1問題はアプリのパフォーマンスを低下させる原因となるので注意しましょう。RailsのN+1問題については、こちらの書籍でも触れているのでぜひ参考にしてみてください。
この記事のまとめ
- n+1問題は、データベースからデータを取り出す際に、大量のSQLが実行されて動作が重くなるという問題のこと
- アプリのパフォーマンスを低下させる原因となるので注意が必要!
- アソシエーションの関連付けを事前に取得するincludesメソッドを使って解決しよう!