実際の実装方法

   本稿の実装例では、アクセスするWebサーバーを固定化するのではなく、セッション情報の保存先を固定化してHTTPセッションの永続性を確保することにします(リスト3)。

リスト3:PHPのPostgreSQL用セッションセーブハンドラの実装例

// pgsql-session.php

/*
セッションテーブル:各sessionデータベースにあらかじめ作成
CREATE TABLE php_session (
session_idvarchar(32)NOT NULL,
i_createdintegerNOT NULL,
i_activeintegerNOT NULL,
t_remote_addrvarchar(20)DEFAULT '',
t_session_datatext,
PRIMARY KEY (session_id)
);
*/

define('SESS_SV', 2); // セッションを保存するDBの数
// DB接続文字列。実際にDBホスト名はsession0, session1 ... session#の形式となる
define('SESS_DB','dbname=session user=yohgaki host=session');
define('SESS_TABLE','php_session'); // セッション情報テーブル

// セッションセーブハンドラを登録
session_set_save_handler (
'pg_session_open',
'pg_session_close',
'pg_session_read',
'pg_session_write',
'pg_session_destroy',
'pg_session_gc'
);


function pg_session_open ($save_path, $session_name) {
return true;
}

function pg_session_close() {
global $session_db;
pg_query($session_db,'COMMIT;');
return true;
}

function pg_session_read ($session_id) {
global $session_db;

if (strlen($session_id) != 32) {
return '';
}
// セッションID(MD5)の最初の5文字のアスキー値の足し算の結果を
// 利用するセッションDBのモジュロを算出し負荷を分散
$sum = 0;
for($i=0; $i5; $i++) {
$sum += ord($session_id{$i});
}
$session_db = pg_pconnect(SESS_DB.$sum%SESS_SV);


$session_id = pg_escape_string($session_id);
$result = pg_query($session_db, 'BEGIN TRANSACTION; SELECT * FROM '. SESS_TABLE ." WHERE session_id = '$session_id' FOR UPDATE");
if (!pg_num_rows($result)) {
define('SESSION', false);
return '';
}
$sess = pg_fetch_assoc($result);
define('SESSION', true);
return $sess['t_session_data'];
}

function pg_session_write ($session_id, $session_data) {
global $session_db;
if (strlen($session_id) != 32) {
return false;
}
$session_id = pg_escape_string($session_id);
$session_data = pg_escape_string($session_data);
if (SESSION) {
$query = 'UPDATE '. SESS_TABLE ." SET i_active = ". time() .", t_session_data = '$session_data' WHERE session_id = '$session_id';";
} else {
$query = 'INSERT INTO '. SESS_TABLE ." (session_id, i_created, i_active, t_remote_addr, t_session_data) VALUES ('$session_id', ". time() .", ". time() .", '". $_SERVER['REMOTE_ADDR'] ."', '$session_data');";
}
pg_query($session_db,$query);
return true;
}

function pg_session_destroy ($session_id) {
global $session_db;
pg_query($session_db, 'DELETE FROM '. SESS_TABLE ." WHERE session_id = '". pg_escape_string($session_id). "'");
if (pg_errormessage($session_db)) {
return false;
}
return true;
}

function pg_session_gc ($maxlifetime = 4000) {
global $session_db;
pg_query($session_db, 'DELETE FROM '. SESS_TABLE ." WHERE i_active ". (time() - $maxlifetime));
if (pg_errormessage($session_db)) {
return false;
}
return true;
}

?>

   セッションセーブハンドラのコードは、通常のセッションセーブハンドラとほぼ同じです。違う部分は、負荷分散を行うために利用するセッションデータベースを、セッションIDを基に算出している部分(リスト3の白文字)のみです。リスト4にその部分を抜粋します。

リスト4:セッションデータベースの算出部分

// セッションID(MD5)の最初の5文字のアスキー値の足し算の結果を
// 利用するセッションDBのモジュロを算出し負荷を分散
$sum = 0;
for($i=0; $i5; $i++) {
$sum += ord($session_id{$i});
}
$session_db = pg_pconnect(SESS_DB.$sum%SESS_SV);

   Webサーバーを追加し、セッションデータベースのスケールアウトも必要となった場合にも、セッションデータを保存するデータベースは、必要に応じて追加可能です。

   次回からはキャッシュについて解説していきます。

人気記事トップ10

人気記事ランキングをもっと見る

企画広告も役立つ情報バッチリ! Sponsored

ClickHouse Cloudが実現した広告運用SaaS「ATOM」のデータ基盤革新、パフォーマンスを向上しながら、運用負荷とコストを削減 11月11日 6:20 AIで開発が加速する時代、品質保証はどう変わる? Tricentisが示すAIエージェント活用の実像 11月9日 6:45 悪用までの猶予が「1分」に迫るフロンティアAI時代に開発と本番を貫く脆弱性管理が必要 10月29日 8:30 欧州CRA対応とSBOM運用。AI生成コードのOSS混入問題の対策とは ――スニペット単位で検出する「FossID」「Insignary Clarity」によるSBOM運用 9月16日 6:40 PostgreSQLからTiDBへ、稼働中SaaSで進める段階的DB移行の実践と再設計への展望 9月7日 6:30 機能安全開発におけるGoogleTestの活用を支援!「Parasoft C/C++test CT」で実現する効率化&AI連携 4月14日 6:30 【CNDW2025】Grafanaが明かす「オブザーバビリティの哲学」ー最小限の労力で実用的なインサイトを得るには 1月23日 6:30 【CNDW2025】プロダクト急増に備える基盤刷新 ーウェルスナビがECSからEKSへの移行で得た知見とは 1月15日 6:30 【CNDW2025】250環境を5人で運用、構築時間は30分に ーKINTOテクノロジーズが語るインフラ基盤組織の作り方 2025年12月18日 6:30 新たな自動化で熱視線! AIエージェントの「推論能力」を支える2つのコンポーネントとは? 2025年11月28日 6:30