こんにちは エンジニア の @len です。現在、弊社のサービスの一つである ferret One と OnePage の速度パフォーマンス改善をやらせてもらっていています。今回はそこで学んだことや経験を共有したいと思います
環境
・ Ruby 2.4.4
・ Rails 5.1.5
・ Mongoid 6.3.0
・ MongoDB 3.6.5
STEP 1. スロークエリを見つける
基本的には、Railsのログを頑張って見ればなんとかスロークエリを見つけることができますが、大きなサービスの場合(特にSPA)、結構時間がかかります。そこで、スロークエリを早めに見つけるためにいくつかツールを使います。
自分は主に、2つのツールをよく使います。
-
rack-mini-profiler
どの画面、どのpartial、どのファイル、どの行で(スロー)クエリがあるか出してくれるツールです。
2. Page Load Time (Chrome拡張機能)
ちらっと表示速度を見たい、client-serverどちらが遅いかを見たい時を使います。拡張なので、本番でも使えます。
STEP 2. スロークエリを改善対策
スロークエリの改善といえば、indexを使うとすぐに効果が出せますね。自分も以前はそう思っていました。しかし、indexを使うにもデメリットはあります。indexを使う前に、以下のように他の方法がないか考えてみましょう。
- このクエリは本当に必要なのか?
- このクエリに既存のindexを利用できるか?
- このクエリの結果を絞ることはできるか?(全件検索か、一週間分か一日分か)
STEP 3. indexを作成する
indexの書き方は色々ありますが、それぞれの場合において適切なindexを使い分ければ、より効果を出すことができます。
- partial indexが使える場合は使います。これによってストレージの負荷を減らせます。
class Article
belongs_to: author, option: true
belongs_to: category, option: true
end
Categoryに関係なく、Authorに紐づいたArticleを探したいときは次のクエリを発行します。
Article.where(author_id: 1)
この場合、index author: 1, category: 1
よりindex author: 1
の方が適切です。
検索速度は一緒ですが、ストレージにかかる負荷が少ないです。
-
compound indexが利用できれば、ストレージを減らすことができます
-
compound indexの順位は気をつける
article_1
という名前のArticleを探したいときは次のクエリを発行します
Article.where(name: "article_1", author_id: 1)
index name: 1, author_id: 1
とindex author_id: 1, name: 1
は場合によって、クエリ速度が違います。 -
一時的に使いたいindexなら、TTL Indexを利用します
期間を設定し、それより古くなったら自動でindexを消すことができます
STEP 4. 本番環境で新しいindexを反映する
indexを作成する時、db.createIndex
を使うと、他のデータベースに関連する作業がブロックされてしまうので、メンテナンスではないときは、必ずbackground: true
をつけてください。より長い時間がかかりますが、他の作業は正常に動けます。
最後に
速度を改善するために、色々な方法があります。使おうとする前に、他の方法を試してください。
indexを使うのはデメリットがありますので、リスクをちゃんと理解した上で、indexを利用しましょう。