Railsアプリケーションのクエリ改善を行いパフォーマンスを向上させた話
はじめに
こんにちは、Rehab for JAPANの ui です。
このエントリでは、Railsアプリケーションの速度改善の実践と結果について書いていきます。
ターゲット
Railsアプリケーションを運用していて、パフォーマンス改善を行いたい方
きっかけ
監視は大きなリリースがあった翌日のエラーの早期チェックのためでしたが、
意図しない、全くリリースと関連のない事象が釣れました。
ここまでのものを見つけてしまっては看過できないということで、速度改善にトライしました。
改善ステップ
STEP0: ゴール設定
パフォーマンスが悪い原因は、特定のSQLクエリです。
そのクエリを確認したかったのですが、長すぎてdatadogで確認ができず、localhostで出力しました。
長大なクエリですが、まずは構造を理解を進めます。
もともと数年間見ているサービスなので、すぐ概要は掴めました。
読者がイメージしにくいと思うので、
ざっくりすぎますが構成はこのように複数のLEFT JOIN、複数条件でwhereで絞りを入れいているそこそこ複雑なクエリです。
クエリにexplainを行い、indexを確認しました。
ALL・・・・!特定のテーブルのfullscanを行なっていることがわかります。
このテーブルにはかなりのデータ量があり、大きくパフォーマンスを落としているという仮説を立て
適切にindexを効かせることをゴールとして進めました。
STEP1: indexが該当のテーブルに貼られているか確認。
show index from {tablename}
私は初歩的な部分も確認することが大事だと今までの人生で学んでいます。
こちらは想定通りのindexが貼られていました。
STEP2: クエリを修正し、explainで確認
怪しい部分を手元で修正しながらどの記述がindexの活用を阻害しているか確認しました。
試行錯誤の末にtype=indexが聞くところまで修正しました。
しかしtype=indexは、ほぼfullscanなので、効果は見込めません。
また、クエリを直すことを優先し、修正を相当入れていました。
テスト工数も考えると、これ以上このアプローチ取ることはできない。方針を改めます。
STEP3: 今までの情報、システム知識、ソースコードリーディング、フル活用。よく考えてみる・よく見る
今までの行動をおさらいしつつ、頭の中を整理しながら再度読み込みました。
その中で、クエリの中に「index効いてないテーブル」と「情報構造がほぼ同じ別のテーブル」の存在に着目しました。
方やfullsccan、方やindexが効いています。
差分がどこにあるか読み直しました。
クエリ
2テーブルの結合の仕方に差分がありました。
ソースコード
このクエリを構築しているソースコードを見ると、特殊な結合を行うwhere句を開発者自身で書いていました。
結合の仕方をindex効いているスタイルに揃え、explainを行うと全てにindexが適切に貼られました 🎉
修正範囲も1行で済みました。影響範囲もこのエンドポイントのみだったためテストコストも低く助かりました。
改善結果
該当のエンドポイントが全くスパイクせず、2分かかかっていたユーザーに関しても1秒以内で処理が完了するようになりました。
まとめ
今回の速度改善の学びとしてはいくつかあるのですが思うところは下記2点です。
- 異常発見/報告の価値
- 異常を異常と認識し、発信することの価値
- やってみる価値
- 今回は自分に実装の機運が高まっていたので前のめりにやってみた
Discussion