Railsの最適化に関する10の事柄

http://weblog.textdrive.com/article/175/rails-optimizing-resource-usage

TextDriveで、Optimizing Rails Resource Usageという記事が公開されました。Railsの最適化について10の事柄を挙げています。興味がある人は原文を読んでもらうとして、ここでは軽くサマリー(意訳)を。間違っていたらコメント歓迎!

1. 最小限のFastCGI

開発には1つのDispatcherで十分。A list Apartでも4つのFastCGI Dispatcherで動かしてて速くてloadも0.01だよ。あとFCGIが増えるとDBコネクションも増えるからね。

2. キャッシュを使う

Dispatcherを通さずキャッシュを使う。Railsではとっても簡単にキャッシュ使えるし、期限設定も楽だし。lighttpdだったらこんな風に設定すればOKさ!

url.rewrite = ( "/$" => "index.html", "([^.]+)$" => "$1.html" )

俺注: Railsのキャッシュの仕組みの一つでは、http://example.com/module/action のキャッシュが静的htmlとして http://example.com/module/action.html に作られる。そのためlighttpdに上記の設定をしておくと、http://example.com/module/action にアクセスしたとき http://example.com/module/action.html にリライトされ、静的htmlがあるとそちらが呼ばれるため、Dispatcherを通さなくてすむ。ていうかこの方法使えばRails on CGI(not FastCGI)が現実的になる、かも。

3. 1,2時間以上development環境のFastCGIで動かしてはならない

developmentだとclassがリロードされ、メモリリークが生じるからだ。

4. メモリ消費量を監視する

大抵FastCGI Dispacher一つにつき20〜50-70 Mbのメモリが消費される。メモリリークしてる場合もあるよ。

5. Dispatcherファイルを変更する


デフォルトじゃGCが走らないからdispatch.fcgiを変更しよう。

RailsFCGIHandler.process! nil, 50

とRailsHCGIHandler.proccess!の第二引数に数値を指定することで、その回数Dispatcherが呼ばれたら明示的なGCが走る。development環境は5-10でいいんじゃないかな。

Psychs さんからの指摘で、現在原文ではこの設定はGC を一切実行しなくなるので良くないだろうと訂正されてます。ありがとうございます。

6. ログローテート

log/ 以下のログローテートしようよ。
俺注:ログ消してもいいときは rake clean_logs使うと便利。

7. ユニットテストを書いて走らせる

時間浪費しなくてすむよ。(俺注: わざわざRails立ち上げなくてもtestできるから、なのかな?)

8. 開発中にメモリリークをチェックする

config/enviroments/development.rbに

dependencies.mechanism = :require

って書くとdevelopmentでもメモリリークのチェックができる。Dispatcherが90MB超えるのは決してないし、通常20-70MBに収まる。そうじゃない場合はlighttpdの再起動時のdispatch.fcgiがkillできなかったか、メモリリークが起きてるかだ。

俺注:dependencies.mechanism = :require より、config.cache_classes = true (デフォルトはdevelopment.rbではfalse)にすべき。

9. 繰り返しに注意

ActiveRecordはスマートだけどこんな風に使っちゃダメだよ。

# やっちゃいけない例
#don't do this, write a JOIN query
Post.find(:all).each do |p|
  p.subscribers << @user unless p.subscribers.include?(@user)
end

Post.find(:all).reject{|p| p.comments_closed? }

# こうすべき
Post.find(:all, :conditions=>"comments_closed = 0")

Post.find_all_by_comments_closed(false)

俺注: AR#findで全部取得してからイテレータ内部で条件指定するんじゃなくて、joinやconditionsつけて条件つけて取得すべきと。Webプログラマならかけ出しのころ単純なSQL条件句ですむ場合にも全部のresult取得して各種言語で取捨選択した経験があるはず。俺はあるorz

10. RailsTracバグレポートをウォチ

毎日Railsで開発してるならウォチ必須!
http://dev.rubyonrails.com/report/1
http://dev.rubyonrails.com/report/3

俺注: RSSリーダに登録しとくといいよ