#1. N+1問題の解消
問題例
# users/index.html.erb <% @users.each do |user| %> <%= user.profile.bio %> <% end %>
- この場合、
user.profile
の取得でユーザーごとにSQLが発行される → N+1問題
改善方法
@users = User.includes(:profile).all
includes
を使って関連データをまとめて取得- SQL発行回数を大幅に削減
💡 ポイント
- N+1はRailsアプリの典型的なボトルネック
- Bullet gemを使うと検知が簡単
#2. クエリ最適化
- 不要なカラムを取得しない
User.select(:id, :name) # 全カラム取得は避ける
- where句で絞り込みを行う
User.where(active: true)
- 複雑な計算はDB側で処理
# Rails側で計算(非推奨) users.sum { |u| u.orders.count } # SQLで集計(推奨) User.joins(:orders).group(:id).count
💡 ポイント
- DBへの負荷を減らす → Rails側での処理は最小限に
#3. キャッシュの活用
3-1. ページキャッシュ
# controller caches_page :index
- 静的ページのレンダリングを減らせる
3-2. フラグメントキャッシュ
<% cache @user do %> <%= render @user %> <% end %>
- 部分的に更新されない箇所をキャッシュする
3-3. Rails.cache
Rails.cache.fetch("popular_articles", expires_in: 1.hour) do Article.popular.to_a end
- 計算コストの高い処理結果をキャッシュ
💡 ポイント
- キャッシュは更新タイミングを意識して使う
- Redisなど高速ストレージとの組み合わせが一般的
#4. インデックスの追加
- DBクエリ速度改善の基本
add_index :users, :email, unique: true add_index :orders, [:user_id, :created_at]
- 外部キーや検索に頻繁に使うカラムには必ずインデックスを追加
💡 ポイント
- インデックスは追加しすぎても書き込み性能に影響するため、必要箇所だけに
#5. バッチ処理の導入
- 重い処理は非同期で行う
# ActiveJob class SendNewsletterJob < ApplicationJob queue_as :default def perform(user_id) user = User.find(user_id) NewsletterMailer.send_newsletter(user).deliver_now end end
- SidekiqやResqueと組み合わせることでレスポンスを高速化
💡 ポイント
- Webリクエスト中に重い処理を行わない
#6. ペジネーションの利用
- 大量データを表示する場合は必ずページング
@users = User.page(params[:page]).per(20)
- kaminariやwill_paginateが便利
💡 ポイント
- 一度に全件読み込まない → メモリとDB負荷を削減
#7. その他の改善ポイント
改善項目 | 内容 |
---|---|
メモ化 | memoize を使い、同じ処理を繰り返さない |
プリロード | includes や eager_load で関連データを事前取得 |
SQLの見直し | EXPLAINでクエリプランを確認 |
ログレベル調整 | productionで log_level = :info などにする |
Webサーバ調整 | PumaやUnicornのスレッド数・ワーカー数を最適化 |
#8. まとめ
Railsのパフォーマンス改善は DBアクセスの最適化 + キャッシュ + 非同期処理 が基本です。
- N+1問題をなくす → includes / eager_load
- DBクエリを最小化 → select / where / インデックス
- キャッシュ活用 → Rails.cache / フラグメント / ページキャッシュ
- 重い処理は非同期 → ActiveJob / Sidekiq
- 大量データはページング
💡 ポイント
- まずはボトルネックの特定から
- New RelicやBulletなどのツールで計測 → 改善箇所を見極める