🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Heroku

HerokuはHeroku社が開発と運営を行っているPaaSの名称です。RubyやNode.js、Python、そしてJVMベース(Java、Scala、Clojureなど)の複数のプログラミング言語をサポートしている。

SQL

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

Q&A

解決済

2回答

1988閲覧

Rails レスポンスタイムが遅い

tomtom1

総合スコア168

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Heroku

HerokuはHeroku社が開発と運営を行っているPaaSの名称です。RubyやNode.js、Python、そしてJVMベース(Java、Scala、Clojureなど)の複数のプログラミング言語をサポートしている。

SQL

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

0グッド

0クリップ

投稿2019/12/10 10:43

###実現したいこと
こんにちは。
これまで、特に処理速度を気にせずに開発を進めていたら、Post/indexページのレスポンスタイムが4000msを超えていました。
実現したいことは、ページレスポンスを早くしたいです。
Post/indexでは、Post.allがeachされ、一覧で表示されます。
写真とともに、has_manyの関係にあるlike/favorite/commentの数が表示されています。
###考えていること
色々と調べてみたら、下記のような原因を考えています。
・Userは、CACHEできているがlike/favorite/commentはされていないため、毎回読み込みを行ってしまっている。
・Indexを貼る(しかし、どのModelにどんなindexを張れば良いかわからないです..
Postに、t.index ["title"], name: "index_posts_on_title"
Postに、t.index ["image_name"], name: "index_posts_on_image_name"
なのか..)
・Railsビューに.countは使わない方がいい。

などもっと調べてらわかることも増えてくるとは思いますが、
現状知恵不足で、その解決方法や、果たして本当にそれが原因なのかを把握できずにおります。
宜しければ、ログを見る限りここを直した方がいい。などのご意見を頂ければ非常に助かります。

###コード

View

1<div class="main"> 2 <div class="container"> 3 <% @posts.each do |post| %> 4 <h4><%= link_to(post.title, "/posts/#{post.id}") %></h4> 5 </div> 6 <div class="story-img"> 7 <%= link_to( image_tag(post.image_name.url), "/posts/#{post.id}") %> 8 </div> 9 <div class="story-icon"> 10 <% if Like.find_by(user_id: @current_user.id, post_id: post.id) %> 11 <%= link_to("/likes/#{post.id}/destroy", {method: "post"}) do %> 12 <span class="fa fa-heart icon-size like-btn-unlike"></span> 13 <% end %> 14 <% else %> 15 <%= link_to("/likes/#{post.id}/create", {method: "post"}) do %> 16 <span class="fa fa-heart icon-size like-btn"></span> 17 <% end %> 18 <% end %> 19 <%= Like.where(post_id: post).count %> 20 &emsp; 21 <% if Favorite.find_by(user_id: @current_user.id, post_id: post.id) %> 22 <%= link_to("/favorites/#{post.id}/destroy", {method: "post"}) do %> 23 <span class="fa fa-star icon-size favorite-btn-unlike"></span> 24 <% end %> 25 <% else %> 26 <%= link_to("/favorites/#{post.id}/create", {method: "post"}) do %> 27 <span class="fa fa-star icon-size favorite-btn"></span> 28 <% end %> 29 <% end %> 30 <%= Favorite.where(post_id: post).count %> 31 &emsp; 32 <% if Comment.find_by(user_id: @current_user.id, post_id: post.id) %> 33 <span class="fa fa-comment icon-size comment-btn-unlike"></span> 34 <% else %> 35 <span class="fa fa-comment icon-size comment-btn"></span> 36 <% end %> 37 <%= Comment.where(post_id: post).count %> 38 </div> 39 <div class="memo"> 40 <%= link_to( image_tag(post.user.image_name.thumb.url), "/users/#{post.user.id}") if post.user.image_name.present? %> 41 <%= link_to( image_tag("/user_images/default_user.jpg"), "/users/#{post.user.id}") if !post.user.image_name.present? %> 42 <strong><%= link_to(post.user.name, "/users/#{post.user.id}") %></strong> 43 <% end %> 44 </div> 45</div>

Model

1class Post < ApplicationRecord 2 belongs_to :user 3 has_many :likes, dependent: :delete_all 4 has_many :favorites, dependent: :delete_all 5 has_many :comments, dependent: :delete_all 6end

Controller

1def index 2 @posts = Post.all.order(created_at: :desc) 3 @post = Post.find_by(id: params[:id]) 4end

schema

1create_table "posts", force: :cascade do |t| 2 t.string "title" 3 t.text "content" 4 t.datetime "created_at", null: false 5 t.datetime "updated_at", null: false 6 t.integer "user_id" 7 t.string "image_name" 8 end 9 10 create_table "users", force: :cascade do |t| 11 t.string "name" 12 t.string "email" 13 t.datetime "created_at", null: false 14 t.datetime "updated_at", null: false 15 t.string "image_name" 16 t.string "password_digest" 17 end

localhost上でログを確認。

log

1Started GET "/posts/index" for 127.0.0.1 at 2019-12-10 17:48:43 +0900 2Processing by PostsController#index as HTML 3 Parameters: {"authenticity_token"=>"ODrhc0d544BUC1VlfJPYkI9hA7jaY1RvWEvO6GdWXKNWOWFffJVhFIDb4tiNAKrT1pQQyXLYtwa4GZ4QBSMzDA=="} 4 User Load (0.6ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]] 5 ↳ app/controllers/application_controller.rb:6 6 Post Load (0.9ms) SELECT "posts".* FROM "posts" WHERE "posts"."id" IS NULL LIMIT ? [["LIMIT", 1]] 7 ↳ app/controllers/posts_controller.rb:28 8 Rendering posts/index.html.erb within layouts/application 9 (0.7ms) SELECT COUNT(*) FROM "users" INNER JOIN "friendships" ON "users"."id" = "friendships"."followed_id" WHERE "friendships"."follower_id" = ? [["follower_id", 1]] 10 ↳ app/views/posts/index.html.erb:3 11 Post Load (0.3ms) SELECT "posts".* FROM "posts" ORDER BY "posts"."created_at" DESC 12 ↳ app/views/posts/index.html.erb:10 13 CACHE User Load (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]] 14 ↳ app/models/post.rb:19 15 User Exists (0.2ms) SELECT 1 AS one FROM "users" INNER JOIN "friendships" ON "users"."id" = "friendships"."followed_id" WHERE "friendships"."follower_id" = ? AND "users"."id" = ? LIMIT ? [["follower_id", 1], ["id", 1], ["LIMIT", 1]] 16 ↳ app/models/user.rb:32 17 CACHE User Load (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]] 18 ↳ app/models/post.rb:19 19 Like Load (0.6ms) SELECT "likes".* FROM "likes" WHERE "likes"."user_id" = ? AND "likes"."post_id" = ? LIMIT ? [["user_id", 1], ["post_id", 5], ["LIMIT", 1]] 20 ↳ app/views/posts/index.html.erb:28 21 (0.2ms) SELECT COUNT(*) FROM "likes" WHERE "likes"."post_id" = ? [["post_id", 5]] 22 ↳ app/views/posts/index.html.erb:37 23 Favorite Load (0.6ms) SELECT "favorites".* FROM "favorites" WHERE "favorites"."user_id" = ? AND "favorites"."post_id" = ? LIMIT ? [["user_id", 1], ["post_id", 5], ["LIMIT", 1]] 24 ↳ app/views/posts/index.html.erb:39 25 (0.2ms) SELECT COUNT(*) FROM "favorites" WHERE "favorites"."post_id" = ? [["post_id", 5]] 26 ↳ app/views/posts/index.html.erb:48 27 Comment Load (1.0ms) SELECT "comments".* FROM "comments" WHERE "comments"."user_id" = ? AND "comments"."post_id" = ? LIMIT ? [["user_id", 1], ["post_id", 5], ["LIMIT", 1]] 28 ↳ app/views/posts/index.html.erb:50 29 (0.2ms) SELECT COUNT(*) FROM "comments" WHERE "comments"."post_id" = ? [["post_id", 5]] 30 ↳ app/views/posts/index.html.erb:55 31 CACHE User Load (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]] 32 ↳ app/models/post.rb:19 33 CACHE User Load (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]] 34 ↳ app/models/post.rb:19 35 CACHE User Load (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]] 36 ↳ app/models/post.rb:19 37 CACHE User Load (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]] 38 ↳ app/models/post.rb:19 39 CACHE User Load (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]] 40 ↳ app/models/post.rb:19 41 CACHE User Load (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]] 42 ↳ app/models/post.rb:19 43 CACHE User Load (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]] 44 ↳ app/models/post.rb:19 45. 46. 47POSTの数だけ繰り返し 48. 49. 50Rendered posts/index.html.erb within layouts/application (136.1ms) 51Completed 200 OK in 398ms (Views: 277.1ms | ActiveRecord: 16.6ms)

実際には、Herokuにデプロイしたアプリのログだと、表示するPostも多いため、4135msもかかってしまうのが現状です。

log

1Rendered posts/index.html.erb within layouts/application (4108.5ms) 2Completed 200 OK in 4135ms (Views: 2349.3ms | ActiveRecord: 1775.3ms)

宜しくお願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

問題点1 Posts.all

本番環境に適用する事を考えるとPost.allは件数が大きくなりすぎるのではないかと思います。
kaminariwill_paginateなどのpaginationライブラリを導入することを検討してみるといいかもしれません。

問題点2 View中でのfind_bywhere

View内でApplicationRecord.find_byApplicationRecord.whereを利用するのはViewの責務が曖昧になる問題があります。
さらに今回のようにループ中で用いる事でパフォーマンス的に劣化します。
基本的には関連付けを利用しましょう。

Like.find_by(user_id: @current_user.id, post_id: post.id)

@current_user.likes.exists?(post_id: post.id)
これちょっと自信がないです。@current_user.likes.pluck(:post_id)から絞り込んだほうが速い気も…

ruby

1class User < ApplicationRecord 2 # 省略 3 def like?(post) 4 @like_posts ||= likes.pluck(:post_id) 5 @like_posts.include?(post.id) 6 end 7end

erb

1<% if @current_user.like?(post) %>

ページねーとした場合likes.pluck(:post_id)ですとlikeを全件取得してしまうため
表示範囲に限定する工夫をした方がいいかもしれません。

Like.where(post_id: post).count

post.likes.count
N+1 countの問題が出ますので対処が必要です。

等など

問題点3 無駄な@post = Post.find_by(id: params[:id])

Controllerにて@post = Post.find_by(id: params[:id])を行っていますが
その後全く使っていないどころか、params[:id]nilのようで、どういった理由で何のデータを取ってきているのかわかりません。

投稿2019/12/11 01:05

asm

総合スコア15149

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tomtom1

2019/12/11 08:18

ご丁寧にご回答頂き、ありがとうございます。 【問題1】実はkaminariを導入しようとしているのですが、上手く行っておりません。 もし宜しければ、こちらもご確認頂けないでしょうか。 https://teratail.com/questions/227132 【問題2】 .find_by:解決致しました。ありがとうございます。 .where.count:ControllerのPost.allにincludesを加える処理が必要ということでしょうか。現状、winterboumさんに回答しました状況で、AVOID eager loading detectedを受けています。 【問題3】 ありがとうございます。その通りなので、削除致しました。
guest

0

これは 1+N 問題と言われているものです
@posts = Post.includes(:user).all.order(created_at: :desc)
として下さい。
この問題を検出してくれるtoolがあります。bullet で探して下さい

やり直し
post.user.name という参照があるのになぜ 不要と言われたのか、入れなくてもpopupしなかったのか、は疑問。
ここにないcodeでなにか有るのかもしれない。
で、
viewの中でLike.where とか Comment.find_by とかDBのアクセスが post毎に行われているのも原因です。
Post.includes にそれらを加えることと、場合によっては joinsして@current_user.id でのしぼりこみも行うという必要があるかも。
風邪気味なのであまりじっくり取り組めないので、上をヒントに工夫して下さい。
bulletにたどり着けるくらいの力があるから、大丈夫そう

投稿2019/12/10 12:17

編集2019/12/10 20:41
winterboum

総合スコア23567

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tomtom1

2019/12/10 14:39

ご回答ありがとうございます!実はその可能性もないかと思い、bulletを導入し、検出していたものの、ポップアップは出てきませんでしたので、よくわからずにいました。 また、今回.includes(:user)を追加しましたところ、下記の内容のポップアップが出てしまいました。 AVOID eager loading detected Post => [:user] Remove from your finder: :include => [:user] Call stack なぜこのような警告がでるのでしょうか?ご指摘頂ければ幸いです。
winterboum

2019/12/10 20:33 編集

ああ、code見ずに書いてしまったので、そうなっちまった。 post.user 以外にも問題有るのかも。やり直し
tomtom1

2019/12/11 08:37

ここにないcodeでなにか有るのかもしれない。 →まずはこちらの原因を探して、解決した後に、Like.whereのN+1問題をPost.includes にそれらを加えることと、場合によっては joinsして@current_user.id で解決していくという理解で宜しかったでしょうか? https://qa.atmarkit.co.jp/q/3369 こちらのサイトのように、外部結合と内部結合の違いがあるかもしれないなど見ましたが、こういった原因は考えられますでしょうか?現状、ここにないcodeの中に原因があるかもしれないとのことで、色々と見直して見ましたが、特に関係性がありそうなコードは見つかっていません...。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問