受託開発しているサイトのアクセス数が、携帯サイトのオープンを機に思いのほか伸びまくりで、時々、503 エラーを出すようになりました。とりあえず処理が重そうな部分をなんとかしましょうということで、手をつけずにいたセッション保存ファイルの処理を変更することにしました。
まず、セッション保存ファイルが現状で何個くらい生成されているかをチェックしてみると…明らかにセッションの復元処理だけで高負荷になるであろうと予想出来るほどの数が単一のディレクトリに作成されていました…すみません…。
対策としては、
- セッションを MySQL などのデータベース上に保存する
- セッションを階層化ディレクトリ下に分散して保存する
の 2つがあります。ベストは 1. なのですが、「今までsession_set_save_handler+データベースでセッションが構築出来たことがない」という記述があったりして…現状動作のシステムに対して早急に対策が必要なので、のんびり検証という訳にもいかず、1. は要検討ということで今回は 2. で行くことにしました。
階層化ディレクトリにセッションが保存されるように、セッション保存パスの設定を以下のように設定します。 (( 僕の場合は、システムの初期化処理内で行なっています。php.ini 内で session.save_path 設定ディレクティブに値を指定してもOKです。))
session_save_path("2;/var/lib/php/session/sysname");
上記のコードは、”/var/lib/php/session/sysname” 以下の階層化ディレクトリ 2階層までを使ってセッションを保存する、という意味です。具体的には、/var/lib/php/session/sysname/0/0/ などのディレクトリが自動的に選択されセッションの保存に使用されます。テストしてみたところ、0 の部分は、0~9, a~z a~f のいずれか 1文字。
この設定を生かせば、単一ディレクトリにドカッとセッション保存ファイルが作成されることは防げます。ただし、注意点が 2つ。
1つ目は、上記の階層化ディレクトリは自動的に作成されないので、予め作成しておく必要がある、ということです。PHP のソースがあれば、ext/session/mod_files.sh というシェルスクリプトでディレクトリを一括作成できます。僕は PHP をソースからコンパイルしていませんので、適当に探してきました。
ところが、これのソースを見れば分かるのですが、
[shell]
<del>for i in a b c d e f 0 1 2 3 4 5 6 7 8 9; do</del>
<del> newpath="$1/$i"</del>
<del> mkdir $newpath || exit 1</del>
<del> sh $0 $newpath `expr $2 – 1`</del>
<del>done</del>
[/shell]
あれ?なんで “f” まで?… “z” まで作成されるように変更してから以下のように実行します。
# sh mod_files.sh /var/lib/php/session/sysname 2
作成されたディレクトリはユーザー apache が書き込み出来るようにパーミッションを変更します。
2つ目は、1階層以上を指定すると(=階層化ディレクトリを使用すると)、自動ガーベッジコレクションは機能しなくなる、ということです。cron などで定期的に古いセッション保存ファイルを削除する必要があります。以下のようなスクリプトを作成して、1日 1回起動するようにしました。
[shell]
find /var/lib/php/session/sysname/ -type f -mtime +1 -name ‘sess_*’ | xargs rm
[/shell]
以上で、PHP セッションを階層化されたディレクトリに保存することが出来るようになりました。