phpのsession fileをN-level階層のdirectory以下に保存

調べようと思って放置して忘れてた。(filesystemによるが)linuxだと1つのdirectory以下に数万程度のfileを保存するとperformanceが落ちてしまうので、accessが集中する場合などにどう対応するかっていう話。特にNFS上にsessionを置くときとかに。

ref.

  • php.iniのsession.save_path の項目。

設定

session.save_pathの値に、pathの前に";"区切りで整数を書く。

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

これで、sess_0123456789abcdefghijklmnopqrstuv というsession fileは
/path/to/session/0/1/2/sess_0123456789abcdefghijklmnopqrstuv に保存される。ただし、php.iniを設定するだけでは動作しないので次の2点に注意。

phpはsub directoryを自動生成しない

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.

とある。なのでPHPSRC/ext/session/mod_files.sh のscriptを使って、あらかじめsub directoryを作成する必要がある。さらに、作成されたdirectoryをapache起動userに変更する。(もしくはsuしてからdirectoryを作成する)

# cd PHPSRC/ext/session
# chmod +x mod_files.sh
# su apache -s /bin/bash
#####            path             session_level   php_version? 
$ ./mod_files.sh /path/to/session 3 5

最後の引数が謎だけど、シェルを見る限りphpのversionかな?
sessionに使う文字が

  • 5未満だと 0-9a-f
  • 5以上だと 0-9a-v
  • 6以上だと 0-9a-zA-Z-,

という風に変更しているので。

phpはsub directory以下のsession file は garbage collectionの対象にしない

NOTE 2: See the section on garbage collection below if you choose to use subdirectories for session storage

cronを使って、更新がされていないsession fileを定期的に削除する。-mtime, -mminとかの数値は適当に。

find /path/to/session -type f -mtime +1    -name 'sess_*' -delete
find /path/to/session -type f -mmin  +1440 -name 'sess_*' -delete

など。OS?によってはfind -deleteが使えないので、その場合は

  • -exec rm {} +
  • -exec rm {} \;
  • パイプ通して xargs rm

とかで。


おまけ: mod_files.sh の中身

#! /bin/sh

if test "$2" = ""; then
        echo "usage: $0 basedir depth"
        exit 1
fi

if test "$2" = "0"; then
        exit 0
fi

hash_chars="0 1 2 3 4 5 6 7 8 9 a b c d e f"
if test "$3" -a "$3" -ge "5"; then
  hash_chars="$hash_chars g h i j k l m n o p q r s t u v"
  if test "$3" -eq "6"; then
    hash_chars="$hash_chars 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 - ,"
  fi
fi

for i in $hash_chars; do
        newpath="$1/$i"
        mkdir $newpath || exit 1
        sh $0 $newpath `expr $2 - 1` $3
done