作ったWebサービスの表示速度を約20倍にした方法について(Ruby on Rails編)
ども。先日ようやく作ってたWebサービスを公開した昨今です。自分の好きなアニメを評価してランキング形式で共有できるWebサービスなので良かったら使ってみてね!
んで、いざ作り終わって公開したところ、ログイン直後の評価対象アニメを一覧するページの表示がめっちゃ遅い。具体的にいうと表示に10秒以上かかってました。ダイアルアップ接続時代を彷彿とさせる表示速度です。というわけで、改善策を色々考えてためしてたんですが、一つ上手くいって実際に早くなったので、その方法を共有したいと思います。
その速度、実に約20倍速。
表示が遅い原因:ページ読み込み時にSQLを大量発行していた
対応策の前に、表示が遅かった原因なのですが1回のページ読み込みの際、内部でSQLが2,000回以上発行してました。そら遅いわ。
以下のように@animesに格納したアニメタイトル約2,000件に対して、@getanimesに含まれるタイトルを抽出するために、
「@getanimes.find_by(title: anime.title)」を実行する記述をしていました。
・controllerファイル
# 全アニメタイトル @animes = Anime.all # ユーザが選択済みのアニメタイトル @getanimes = GetAnime.find_by(uid: @current_user.id)
・viewファイル
# @animesに格納されている約2,000タイトルを読み込むまで繰り返す <% @animes.each do | anime | %> # 既に選択済みの場合はページ表示の際にチェックを入れる # ここで「select * from ~ 」のSQLがタイトル数(約2,000)分実行される <% if @getanimes.find_by(title: anime.title) != nil %> <%= check_box "animelist", "#{anime.title}", :checked => true %> <% else %> <%= check_box "animelist", "#{anime.title}" %> <% end %> <% end %>
対策:抽出対象の文字列を全て配列に格納して「find_by」の代わりに「include?」を使用する
というわけで、なんとかSQL発行回数を減らせないかなと考えていたのですが、抽出対象の@getanimesの内容を一度すべて配列に格納して、「find_by」の代わりに配列に文字列が存在するかを確認する「include?」を使用したところ劇的に速度が速くなりました。
・controllerファイル
@animes = anime.all @getanimes = Getanime.where(uid: @current_user.id) # @getanimesのタイトルを全て配列に格納 i = 0 @ararry_getanimes = [] @getanimes.each do | anime | @ararry_getanimes[i] = anime.title i = i + 1 end
・viewファイル
<% @animes.each do | anime | %> # 配列内に対象のタイトルが存在するか「include?」で確認するに変更 <% if @ararry_getanimes.include?(anime.title) %> <%= check_box "animelist", "#{anime.title}", :checked => true %> <% else %> <%= check_box "animelist", "#{anime.title}" %> <% end %> <% end %>
まぁ、毎回DB(外部ディスク)からデータを引っ張ってくるより、配列(メモリ)にデータを読み込んだあとにあれこれした方が早くなるんだろうなと解釈してます。あまり大量データに対してこれやると、メモリ不足で逆に遅くなるかもですが…。
最後に
とはいえ、2,000タイトル以上を読み込んで整形する必要があるため、まだまだ速度は遅いなぁと感じています。何か他に良い方法があれば教えてください!!