日々精進

新しく学んだことを書き留めていきます

Rails - Advanced Railsのパフォーマンスチューニング技法2

昨日の続き。ここから使えそうなテクニックが登場しだす。


3.Active Record(AR)のパフォーマンスについて
・過剰なインスタンス
ARはfindメソッドで取ってきたレコードをすべてインスタンス化するので重い。↓のようにARを通さずにレコードを取得すればパフォーマンスは向上する。ただし、結果はStringで返される。

class <<ActiveRecord::Base
  def select_values(sql)
    connection.select_values(sanitize_sql(sql))
  end
end

sql = %(SELECT id FROM people WHERE last_name = ?)
last_name = %(O'Reilly)

Person.select_values [sql, last_name] # => ["12", "42"]



・1+N問題(無駄なSELECT文)
以下の例で説明する。

class Person < ActiveRecord::Base
  has_one :user
end

class User < ActiveRecord::Base
  belongs_to :person
end

上記のクラスに対して以下のコードを実行する。

logins_from_illinois = []
Person.find_all_by_state('IL').each do |person|
  logins_from_illinois << person.user.login
end

この時、person.user.loginが実行される度にpersonに紐付くuserが検索されるので以下のようなクエリが実行される。

SELECT * FROM users WHERE person_id = 4;
SELECT * FROM users WHERE person_id = 17;
SELECT * FROM users WHERE person_id = 36;
SELECT * FROM users WHERE person_id = 39;

このクエリをまとめるには、Person.find_all_by_state('IL')で実行されるクエリにusersをjoinすればいい。

SELECT * FROM people LEFT JOIN users ON people.id = users.person_id
WHERE people.state = 'IL';

これは:includeオプションを使えば簡単に実現できる。

logins_from_illinois = User.find(:all, :include => :person, :conditions => "people.state = 'IL'").map(&:login)

さらに多段includeも可能らしい。ココに詳細あります。


4.インデックス
Railsは助けられない問題なので自分でなんとかしよう。
外部キーにインデックスをつけるべし。
Explain句なんかを使ってクエリを調査する。
全文検索ならFulltext index。MyISAMについてるFulltext index機能よりもFerretというソフトがいいらしい。acts_as_ferretプラグインを使えば簡単に導入できる。
位置情報は特殊なインデックスが必要。

 ANALYZE TABLE table_name 
 OPTIMIZE TABLE table_name

↑を使ってまめにIndexの最適化をしよう。




とりあえず1+N問題、インデックスの追加、Ferret導入をしてみよう。Ferretは導入できるかが心配だけど。。