昨日の続き。ここから使えそうなテクニックが登場しだす。
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は導入できるかが心配だけど。。