とあるシステムのセッションディレクトリを覗いてみたらミリオン単位のファイルが詰まっていることが解って、I/O重いの当たり前じゃねーか…!(普通は1ディレクトリ1000ファイルまでに抑えるべき)となったので改善を試みました。

具体的にはディレクトリを階層化します。

改善前

/path/to/session/sess_hogehogehoge

改善後

/path/to/session/0/1/2/sess_hogehogehoge


以下、手順。

  1. 階層化されたセッションディレクトリを作成する
  2. 作成したセッションディレクトリのパーミッションを変更する
  3. PHPの設定を変更する
  4. httpd再起動
  5. 古いセッションを削除するように設定する

1. 階層化されたセッションディレクトリを作成する

セッションディレクトリの設定自体はPHPの設定ファイル(php.ini)で行うのですが、その設定部分に以下のような注意書きがあります。

; NOTE 1: PHP will not create this directory structure automatically.
; You can use the script in the ext/session dir for that purpose.

簡単に言うと格納先のディレクトリがないからといってPHPがそのディレクトリを自動的に作ったりしないので、あらかじめ階層化されたディレクトリを用意しておいてね、ということですね。そのためのツールはPHPのソースに含まれているということなのですが、yumで入れているのでありません。というわけで、こちらから「mod_files.sh」というファイルをダウンロードしてきます。

[svn] Index of /php/php-src/trunk/ext/session

現在のバージョンはPHP5用。検索で見つかるページにはPHP4用をカスタマイズして使用しているページもありますが、そんなことする必要は特にありません。これでOK。

コマンドはこんな感じ。

# sh mod_files.sh /path/to/session [階層] [バージョン]

「階層」は文字通り階層の数。例えば3にするとこんな感じになります。

/path/to/session/0/1/2/sess_hogehogehoge

「バージョン」はディレクトリにどんな文字を使用するか。これには3種類あり、以下のようになっています。

「6」を指定した場合 … 64文字

0 1 2 3 4 5 6 7 8 9 a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z - ,

「5」を指定した場合 … 32文字

0 1 2 3 4 5 6 7 8 9 a b c d e f g h i j k l m n o p q r s t u v

指定しないまたは[5]「6」以外を指定した場合 … 16文字

0 1 2 3 4 5 6 7 8 9 a b c d e f

一応、この数字はPHPのバージョンということになっており、ハッシュファイルに使われる文字と合致することになっています。ただPHP5でも64文字を使ってセッションファイルを生成している場合もあるとのこと。実際に作成されているファイルを書き出して検証してみたところ、多くはvまでしか使用しておらず特に先頭3文字はvまでしか使用していませんでしたが、中には64文字使用しているものもあり、念のため「6」に設定しておきました。

なお作成するディレクトリの数を増やせば増やすほど作成に時間が掛かります。



2. 作成したセッションディレクトリのパーミッションを変更する

上記手順でディレクトリを作成すると、rootの0755で作成されます(多分)。で、Apacheユーザーが権限を与えられていない場合セッションファイルを作成することが出来ません。
というわけで、必要があればパーミッションを設定してやります。



3. PHPの設定を変更する

php.iniファイルの「session.save_path」を編集します。注意書きは以下の通り。

; session.save_path = "N;/path"
;
; where N is an integer. Instead of storing all the session files in
; /path, what this will do is use subdirectories N-levels deep, and
; store the session data in those directories. This is useful if you
; or your OS have problems with lots of files in one directory, and is
; a more efficient layout for servers that handle lots of sessions.

簡単に言うと、コロンで区切って整数とディレクトリを記述しクオーテーションでくくる。

こんな感じ。

session.save_path = "3;/path/to/session"



4. httpd再起動




5. 古いセッションを削除するように設定する

以上で完了なのですが、セッションを階層化すると古いセッションを自動的に削除する仕組みが動かなくなるので、ユーザー側で削除してやる必要があります。cronで処理してやれば良いみたい。

PHPのSESSIONファイルを階層化されたディレクトリに保存する | 携帯サイトをつくろう。

find /path/to/session -type f -mtime +1 -name 'sess_*' | xargs -r rm

このコマンドの意味は「セッションディレクトリ以下において1日以上変更がない『sess_』で始まるファイルを再帰的に削除する」。

ただまぁプログラムの都合上セッションファイルを1日で削除されても困るので、-mtimeオプションのところを適当な長さにして実行ファイルを作成し、/etc/cron.dailyに放り込みました。



というわけで、設定変更OK。

多少はI/Oが軽くなってくれると助かる。期待はしてないけど。