SQLログ確認と最適化の全体像

1. なぜSQLログを確認するのか

Rails アプリのパフォーマンス劣化の多くはデータベースアクセスに起因します。SQLログを定期的に確認することで、次のような問題を早期に特定できます。

代表的な症状 典型的な原因
ページ描画が遅い 不要なクエリ発行(N+1)
CPU 使用率が高い インデックス不足による全表スキャン
DB 接続数が多い コネクションプール設定ミス、肥大化したトランザクション

2. Rails で SQL ログを出力・収集する方法

環境 設定/手順 補足
開発環境 config/environments/development.rb
config.active_record.verbose_query_logs = true
発行元ファイルと行番号をログに付与
本番環境 config/environments/production.rb
config.log_level = :info 以上
config.active_record.logger = ActiveSupport::Logger.new(STDOUT)
低レベルの :debug はログ量激増に注意
外部ログ集約 Fluentd / Logstash 経由で ElasticSearch などに転送 Kibana で遅いクエリを可視化

Slow Query ログ
RDBMS 側の設定も併用すると効果的です。例:PostgreSQL なら log_min_duration_statement = 200ms


3. SQL ログの読み方

plaintext
User Load (3.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 42], ["LIMIT", 1]]
部分 意味
User Load モデル名とクエリ種別
(3.5ms) DB サーバーが返答するまでの時間
SELECT ... 実行された SQL
[[...]] バインドパラメータ

着目ポイント

  1. 実行時間: 100 ms を超える行は要調査。

  2. 頻度: 1 ページ表示につき何度同じクエリが出ているか。

  3. パラメータ差し替え: 同じ SQL でもバインド値が異なるだけならキャッシュ検討。


4. 典型的なボトルネックの検出

パターン 具体例 検出ヒント
N+1 問題 `Book.all.each { b
インデックス不足 WHERE email = ? で全表スキャン (0.nnnms) が極端に長い
肥大 JOIN includes(:comments).where(comments: { spam: false }) EXPLAIN で Hash/Sort が多重に発生

5. ログ解析を補助する仕組み

  1. ActiveSupport::Notifications

    ruby
    ActiveSupport::Notifications.subscribe('sql.active_record') do |*args| event = ActiveSupport::Notifications::Event.new(*args) puts "[#{event.duration}ms] #{event.payload[:sql]}" end
  2. Rack Mini Profiler
    画面下部でリクエストごとの SQL 一覧と合計時間を確認。

  3. Bullet

    • N+1 や不要な eager loading を自動検出。

    • 開発時にブラウザ通知やログ出力。

  4. pgHero / rails-pg-extras(PostgreSQL)
    インデックス提案、肥大テーブル、バッファヒット率を一覧表示。


6. 実行計画(EXPLAIN)による深掘り

bash
EXPLAIN ANALYZE SELECT * FROM users WHERE email = 'a@example.com';
見るべき指標 目安
Seq Scan 行数が多ければインデックス検討
Index Scan 期待通りのカラムか
Total Cost / Actual Time 高いほど改善余地

Rails では

ruby
User.where(email: 'a@example.com').explain

で Ruby から直接確認可能。


7. 具体的な最適化アプローチ

問題 解決策
N+1 includes, preload, eager_load を適切に使う
部分列だけ必要 select(:id, :name) で不要列を省く
LIKE パターン前方一致 WHERE name LIKE 'abc%' + 先頭ワイルドカードを避ける
日時範囲検索 複合インデックス (user_id, created_at) を検討
集計クエリ頻発 Counter Cache、Materialized View、キャッシュストア利用

8. ログローテーションと保管戦略

  • logrotate で日次圧縮・世代管理(例:/var/log/rails/*.log)。

  • Rails 標準 rotate: config.logger = Logger.new('log/production.log', 10, 100.megabytes) で世代数とサイズ指定。

  • PII マスキング: GDPR や個人情報保護の観点から、ログ出力前にフィルタリング (config.filter_parameters) を必ず設定。


9. CI/CD パイプラインへの組み込み

ステップ 目的
bundle exec brakeman SQL インジェクション検出
bundle exec bullet --check N+1 回帰防止
rails test + EXPLAIN 差分自動比較 クエリプランの悪化を PR でブロック

まとめ

  1. **まずは SQL ログを“見える化”**して実行時間・発行回数を計測。

  2. N+1 やインデックス不足など典型的な課題を Bullet や Rack Mini Profiler で検出。

  3. EXPLAIN と RDBMS の slow query log で深掘りし、インデックス追加・クエリ改善を行う。

  4. ログローテーション・マスキングを整えた上で、CI 経由の自動チェックでパフォーマンス劣化を未然に防ぐ。

これらを習慣化することで、Rails アプリのデータベースレスポンスを継続的に最適化できます。

ChatGPT4o 生成日:2025/06/21