PHPのセッション管理に使う箱選び 2

前回からの続き

MySQL

DBにはMySQL-5.0.27を採用。
MyISAMInnoDBで比較してみる。MEMORY(HEAP)は試していない。MySQLサーバへはUNIXドメインソケットを使って接続し、毎回接続/切断を行うようにしてみた。GCは1/100のまま。


設定の問題もあるかもしれないけど、リクエスト数が増えるとMyISAMの場合は順調に遅くなってしまう。一方のInnoDBはかなり安定した性能。
MyISAMの場合、DELETEを実行する際にテーブルをロックしてしまうため、レコード数が多い状態で全件走査するDELETEが実行されると、長い時間他のクエリが待たされてしまうのが遅くなる原因ではないかと思う。


最長待ち時間もリクエスト/秒と同じ傾向になっている。ひとつのGCの処理が終わる前に別のGCが起動するという状態が繰り返され、どんどん待ち時間が長くなっているみたい。

設定など

適当に設定したmy.cnf

skip-locking
key_buffer = 64M
max_allowed_packet = 1M
table_cache = 64
sort_buffer_size = 512K
net_buffer_length = 8K
read_buffer_size = 1M
read_rnd_buffer_size = 512K
myisam_sort_buffer_size = 1M

innodb_data_file_path = ibdata1:10M:autoextend
innodb_buffer_pool_size = 64M
innodb_additional_mem_pool_size = 8M
innodb_log_file_size = 32M
innodb_log_buffer_size = 8M
innodb_flush_log_at_trx_commit = 1
innodb_lock_wait_timeout = 50

テーブルのCREATE文

CREATE TABLE `sess_inno` (
  `id` varbinary(48) NOT NULL,
  `data` blob NOT NULL,
  `mtime` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=binary

CREATE TABLE `sess_myisam` (
  `id` varbinary(48) NOT NULL,
  `data` blob NOT NULL,
  `mtime` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=binary

セッションハンドラ

<?php

define('DB_HOST', 'localhost');
define('DB_NAME', 'session');
define('DB_USER', 'httpd');
define('DB_PASSWORD', 'password');

$GLOBALS['sess_con'] = null;

function open($save_path, $session_name)
{
    $con = mysql_connect(DB_HOST, DB_USER, DB_PASSWORD);
    if ($con !== false) {
        mysql_select_db(DB_NAME, $con);
        mysql_query('SET NAMES binary', $con);
        $GLOBALS['sess_con'] = $con;
        return true;
    } else {
        return false;
    }
}

function close()
{
    return mysql_close($GLOBALS['sess_con']);
}

function read($sess_id)
{
    $con = $GLOBALS['sess_con'];
    $sql = sprintf("SELECT data FROM sess_myisam WHERE id = '%s'",
        mysql_real_escape_string($sess_id, $con));
    $result = mysql_query($sql, $con);
    if ($result !== false && mysql_num_rows($result) === 1) {
        $row = mysql_fetch_row($result);
        return $row[0];
    } else {
        return '';
    }
}

function write($sess_id, $sess_data)
{
    $con = $GLOBALS['sess_con'];
    $sql = sprintf("REPLACE INTO sess_myisam (id, data) VALUES('%s', '%s')",
        mysql_real_escape_string($sess_id, $con),
        mysql_real_escape_string($sess_data, $con));
    $result = mysql_query($sql, $con);
    if ($result !== false) {
        return true;
    } else {
        return false;
    }
}

function destroy($sess_id)
{
    $con = $GLOBALS['sess_con'];
    $sql = sprintf("DELETE FROM sess_myisam WHERE id = '%s'",
        mysql_real_escape_string($sess_id, $con));
    $result = mysql_query($sql, $con);
    if ($result !== false) {
        return true;
    } else {
        return false;
    }
}

function gc($maxlifetime)
{
    $con = $GLOBALS['sess_con'];
    $sql = sprintf("DELETE FROM sess_myisam WHERE mtime < FROM_UNIXTIME(%d)",
        time() - $maxlifetime);
    $result = mysql_query($sql, $con);
    if ($result !== false) {
        return true;
    } else {
        return false;
    }
}

session_set_save_handler('open', 'close', 'read', 'write', 'destroy', 'gc');

?>

destroy関数で使っているmysql_real_escape_string()の前に$が付いていたので削除