質問をすることでしか得られない、回答やアドバイスがある。

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

新規登録して質問してみよう
ただいま回答率
85.48%
Ruby

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

Ruby on Rails

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

Q&A

解決済

1回答

1916閲覧

Railsでhas_manyによるメソッドを使うのではなく、サブセレクトを使う理由

mitsuru793

総合スコア157

Ruby

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

Ruby on Rails

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

0グッド

2クリップ

投稿2015/08/16 15:51

現在、Ruby on Rails チュートリアルのソースコードをgithubからダウンロードして、勉強のために閲覧しています。 Twitterのようにつぶやいたことを表示するアプリです。

下記のself.from_users_followed_byは引数に受け取ったuserがフォローしているユーザーがフォローしているユーザーの投稿を取得するものです。

ruby

1class Micropost < ActiveRecord::Base 2 def self.from_users_followed_by(user) 3 followed_user_ids = user.followed_user_ids 4 where("user_id IN (?) OR user_id = ?", followed_user_ids, user) 5 end 6end

このメソッドだとフォローしているユーザーが5000人以上だとスケールアップができないため、次のようにサブセレクトを使い対応すると書いてありました。

ruby

1class Micropost < ActiveRecord::Base 2 def self.from_users_followed_by(user) 3 followed_user_ids = "SELECT followed_id FROM relationships 4 WHERE follower_id = :user_id" 5 where("user_id IN (#{followed_user_ids}) OR user_id = :user_id", 6 user_id: user.id) 7 end 8end

もとのfollowed_user_ids = user.followed_user_idsの方法だと、全てのフォローしているユーザーidの配列がrubyのメモリの中に配置されてしまうのが問題なのでしょうか?それがサブセレクトを使うと、ruby側の余分なメモリ確保を防ぐことができると思いました。しかし、サブセレクトでもidを取得した際にメモリ確保はされると思います。

followed_user_ids = user.followed_user_idsの問題は上記の解釈で合っていますでしょうか?
ご回答、よろしくお願い致します。

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

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

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

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

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

guest

回答1

0

ベストアンサー

ruby側の余分なメモリ確保を防ぐことができる

というのは、その通りです。

チュートリアルで書かれていますが、サブセレクトを使用しない方のfollowed_user_idsは、ActiveRecordが自動的に動作するメソッドです。
内部的には、
user.followed_users.map{|u| u.id}みたいな動作になります。
この方法だと、まず、user.followed_usersをコールするので、
自動的にfollowed_usersのインスタンスを全てメモリに展開してしまいます。

したのサブセレクトの方だと、usersのid列だけを取得しているので、
その分メモリが使用されないという利点があるはずです。

投稿2015/08/17 00:56

rifuch

総合スコア1901

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

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

mitsuru793

2015/08/17 06:08

rubyには便利なメソッドがありますが、それには色々なメソッドが被せてあるので動作が重くなってしまうのですね。ということはSQLでできる事はなるべくSQLで対応した方が高速になるということですね。 お答え頂き、ありがとうございました。
rifuch

2015/08/17 06:13

Railsはさらっと書くのには向いていますが、ちょっと油断すると、大量のIOが発生したりと気が抜けないです。 そのコードがどのようなIOを発生させるのかとか、どの状態でキャッシュがきくのかとか、そういうことを考える必要がありますね。 ちなみに、上記のようなことであれば、サブクエリを使えばIOを1回で済ませることが出来るので、私だったらそう書きます。
mitsuru793

2015/08/18 02:10

サブクエリとはサブセレクトのことですよね。 Railsは作成するのが手軽で面倒なことを色々やってくれる分、その色々が処理量を増やすことにもなるのですね。またこのIOを減らすのがチューニングに繋がるというイメージが持てました。 ありがとうございます。
rifuch

2015/08/18 02:31

あ、おっしゃるとおりです。なぜか、サンプルコードを見て2回IOが発生すると勘違いしていました。 普段なるべく生のSQLを書かないようにしているので、読み間違えてました。 > IOを減らすのがチューニングに繋がる これは、ある意味その通りです。webシステムの最大のボトルネックはそこにあると言われていますから、大体チューニングで最初に手をつけるのはここですね。
mitsuru793

2015/08/18 04:34

> webシステムの最大のボトルネックはそこにある これはwebはクライアントとサーバーがHTTPリクエストを使って、小さなデータでも遠隔通信を行う必要があるからということですよね。 webの最大のボトルネックはIOというのは初めて聞きました。大変勉強になります。
rifuch

2015/08/18 05:13

この場合のIOは、データベースとのIOです。(IOっていう言い方が悪かったですね) 1リクエストの処理において、DBMSとの通信オーバーヘッドが一番コストを食うので、出来るだけデータベースとの通信は一発で終わるようにするのが良い、という考え方です。 もちろん、サーバーとクライアントの間の通信もボトルネックではあるのですが、見せ方や AJAXを使うことでごまかしはききます。 Railsの場合、リレーションをつけたオブジェクトに対して、メソッドでアクセスできるので、意識せずにビューでループを回したりすると、ループ1回に付き1回DBアクセスが発生したりするので、そのあたりは要注意です。
mitsuru793

2015/08/19 04:57

1回のリクエストにおいての、DBとの通信回数も減らさないといけないのですね。リクエストを減らすだけでなくその先も見ないと。 大変勉強になりました。ありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問