猫の魔法

主にruby系の技術メモを記載

個人的メモ:Rails AntiPatterns(16)

忙し過ぎて時間が空いてしまった。

CHAPTER 8 Scaling and Deploying

AntiPattern: Painful Performance

  • 単純なパフォーマンスの問題を防ぐ為の事例を紹介する。

Solution: Don’t Do in Ruby What You Can Do in SQL

  • RubySQLで出来る事をやらせないようにする
  • レコードの総件数を求める方法は3つ考えられるが、どれも裏側の動きが異なる
@article.comments.count 
@article.comments.length
@article.comments.size
  • 一つ目の例は、DBのcount関数を実行する。
  • 二つ目の例は、lengthがActiveRecordの関連で無い為、すべてのレコードをDBから取得する
  • 三つ目の例はすでにレコードがロードされていればそれを使い、そうでない場合はDBのcountを使う。
  • ケースに応じて使い分ける事が重要。

  • SQLを上手く使えていないパターンとして以下の2パターンがある。

    • SQLで実現する方法を知っているがRubyで書いてしまう。
      • 例えば下記のような例(コードは抜粋)
@account = Account.find(3) 
@users = @account.users.sort { |a,b| a.name.downcase <=> b.name.downcase }.first(5)
  • 上記のコードよりも下のコードの方が、処理をよりDBに移譲できる為パフォーマンスを高められる
@users = @account.users.order('LCASE(name)').limit(5)
  • SQLで実現する方法を知らない為Rubyで書いてしまう。
    • 例えば下記のような例(コードは抜粋)
class User < ActiveRecord::Base
 has_many :comments has_many :articles, :through => :comments

 def collaborators 
   articles.collect { |a| a.users }.flatten.uniq.reject {|u| u == self } 
 end 
end
  • 上記よりも、下記の方がより処理をDBに任せる事が出来る。
    • このパターンはflattenを使うような処理になってしまっている場合に置き換えが出来る事が多い
    • ★?の部分は、順番に配列の第2,第3要素が入る。
class User < ActiveRecord::Base
 has_many :comments
 has_many :articles, :through => :comments
 
 def collaborators User.select("DISTINCT users.*").
        joins(:comments => [:user, {:article => :comments}]).
           where(["articles.id in ? AND users.id != ?", self.article_ids, self.id]) 
 end 
end

Solution: Move Processing into Background jobsの前まで読了。

関連

個人的メモ:Rails AntiPatterns(一覧) - 猫の魔法