TechNote

事務屋のおぼえがき

PHPからociでOracleデータベースへ接続するときに注意したセッション数まわりのはなし

前エントリー「PHPからociでOracleデータベースへ接続する方法と注意点まとめ - TechNote」で、PHPからociでOracleデータベースに接続したときのメモを残しましたが、実際にociを使用して既存システムのデータベースに接続をするときには「セッション数」を十分に考慮しなくてはなりません。ということでセッション数を確認しながらテストした過程のメモ。

f:id:kojikoji75:20140810210138j:plain
photo credit: pecooper98362 via photopin cc

サマリー

1.セッションとは
2.Oracleでセッション数を確認する方法
3.実際セッションの増え方を観察してみた
4.とった対策




1.セッションとは

ここでいうセッションは、Webのsessionとは違います。
ユーザーがユーザープロセスを介してOracleインスタンスに接続するときの特定の接続のことです。Oracleに接続するとセッションが1つ生成されます。切断すると1つ消滅します。

Oracleでは、セッション数(最大同時接続数)を制限するパラメータがあり、その数をオーバーするような多数の同時接続があった場合、以降の新規接続が確立されなくなります。(それくらいの問題で済んだはず?だと思う。)

Oracleを使ったシステムならそのあたりは十分に検討された設計がなされているはずです。ですが今回のように、単体で動いていた既存システムのOracleDBを活用するような場合には、設計者の意図しない接続数となるわけですから、十分な注意が必要となります。

(セッションに似たような位置付けの言葉に「プロセス」がありますが、ここではセッションだけを見ていくことにして、プロセスの説明は割愛します。)



2.Oracleで最大セッション数と現在のセッション数を確認する方法

(1)sqlplusでシステム管理者としてログインする

v$sessionからセッション情報を得ることができますが、v$sessionを覗くにはまずシステム管理者としてログインする必要があります。

コマンドプロンプトを起動して以下のように打つことで管理者としてログインできます。

squplus system/manager@[接続文字列]


(2)最大セッション数の設定値を確認する

最大セッション数はv$system_parameterにて確認することができます。以下のsqlを発行します。

select name, type, value from v$system_parameter where name = 'sessions'

(3)現在のv$sessionを確認する

現在のセッション数(同時接続数)はv$sessionにて確認することができます。

単純にセッション数を知りたい場合は下記のsqlでv$sessionのレコード数をカウントするだけです。

select count(*) from v$session;

ですが今回は、phpからの接続がどのようにセッションを占有するのかをきちんと見ておきたいので、セッションの内訳を見ていくことにします。

全項目をselectすると、項目数が多すぎてコマンドプロンプトではひと目で確認できないので、selectする項目を絞る必要があります。

ちなみに観察した項目は下記です。

項目名 詳細
SID セッション識別子
SERIAL# セッション・シリアル番号
USERNAME Oracleのユーザー名
STATUS 状態: ACTIVE,INACTIVE,KILLED,CACHED,SNIPED
OSUSER OSのユーザー名
PROCESS OSのプロセスID
MACHINE OSのマシン名
TERMINAL OSの端末名
PROGRAM OSのプログラム名


コマンドプロンプトに結果を表示させてもなかなか追いにくいところがあるので、ファイルに出力することにします。

logに出力するには下記のように打ちます。

spool d\temp\result.log
select username,status,osuser,machine,terminal,program from v$session;
spool off

CSVとして出力するには下記のよう打ちます。

set echo off
set linesize 1000
set pagesize 0
set trimspool on
set feedback off
set colsep ','
spool d:\temp\result.csv
select username,status,osuser,machine,terminal,program from v$session;
spool off

※注意点

「status」のactice/inactiveについて。一見、inactiveだったら「もうレコードだけ残っているだけで、開放されてるってことじゃないの?」と思ってしまいがちですが、inactiveで残っているということはクライアントから接続されていて生きているコネクションということです。コネクションプーリングを行っている可能性があります。よって、単純にセッション数を知りたい場合は、active/inactive関係なくv$session全体をカウントすべきです。


3.実際セッションの増え方を観察してみた

oci接続をするWebシステムをブラウザで起動させたら、v$sessionに以下のようなレコードが追加されました。

sid session# terminal program
41 37140 testsvr httpd.exe

どうやらphpからの接続は、端末名はwebサーバをおいているマシン名、プログラム名は「httpd.exe」となるようです。


試しにもう一個ブラウザを起動して、v$sessionを見てみると

sid session# terminal program
41 37140 testsvr httpd.exe
57 5564 testsvr httpd.exe

2つに増えます。この調子だと社内の全端末でこのwebシステムを起動すると最大接続数をすぐに超えてしまい、既存システムの動作に影響を与えてしまいそうです。(最大接続数を増やすという案はここでは考えません。)

4.とった対策

これに対してとれる対策は、一般的なことですが「不要となった接続は閉じる」こと。データベースリソースを有効に使うためには、接続を閉じる行為は常識です。

oci接続の閉じ方は下記でOKのようです。

oci_close($con);


ですが、わたしの場合CodeIgniterのアクティブレコードを使用しているため、カプセル化された部分できちんとclose処理まで書かれているはずとなので、なぜセッションが残るのかが不思議でした。close処理なんて書く必要ないはずですが、内部の処理を追っていくしかないのかなぁと思ってた矢先、すぐに答えに辿り着きました。

答えはdatabase.phpの設定値にありました。

//変更前のdatebase.php
$db['default']['hostname'] = "(DESCRIPTION =(ADDRESS_LIST =(ADDRESS = (PROTOCOL = TCP)(HOST = svord)(PORT = 1521)))(CONNECT_DATA =(SERVICE_NAME = ORCL)))";
$db['default']['username'] = "username";
$db['default']['password'] = "password";
$db['default']['database'] = "connectstring";
$db['default']['dbdriver'] = "oci8";
$db['default']['dbprefix'] = "";
$db['default']['pconnect'] = TRUE;
$db['default']['db_debug'] = TRUE;
$db['default']['cache_on'] = FALSE;
$db['default']['cachedir'] = "";
$db['default']['char_set'] = "utf8";
$db['default']['dbcollat'] = "utf8_general_ci";

初期状態ではpconnectが"TRUE"になっているので、ここを"FALSE"に変更します。

$db['default']['pconnect'] = FALSE;

pconnectを有効にするということは、oci_pconnect関数を使って接続するということで、持続的接続することを意味します。

ということで、わたしの場合のセッション占有の原因はoci_closeしていないことではなくて、pconnectを使用していたとにありました。

これを修正した結果、以降新しく接続数を増やしても(別のブラウザでいくつ開いていっても)、セッション数は一切増えることはありませんでした。

最後にこれまでのテスト過程で残留したセッションを終了させます。終了させるには以下のようなsqlを書きます。

alter system kill session '[sid],[serialNo]';

事前にv$sessionにて、終了させたいレコードのsid,serialNoを確認してから上記sqlを実行すると、該当レコードのstatusはkilledとなります。killed状態(消滅待ち状態)となったレコードがどのタイミングで消滅するのかは確認できていません。しばらくはkilled状態で残りましたが翌日には消えていました。

環境は下記でした。

  • XAMPP for windows 1.8.1
  • Oracle11g

あわせて読みたい

PHPからociでOracleデータベースへ接続する方法と注意点まとめ - TechNotePHPからociでOracleデータベースへ接続する方法と注意点まとめ - TechNote

photo credit:torkildrviaphotopinccサマリー1.ociとは2.php.iniの設定3.OracleInstant Clientの...

PHPからODBC経由でoracleデータベースへ接続するための記述 - TechNotePHPからODBC経由でoracleデータベースへ接続するための記述 - TechNote

単純な接続方法だが、意外にハマったのでメモを残す。1.COMを使った接続方法(ADODB)2.odbc_connectを使った接続方法1.COMを使った方法(A...

Oracleのtimestamp型のフィールドで大小比較する方法 - TechNoteOracleのtimestamp型のフィールドで大小比較する方法 - TechNote

基幹システムのDBデータを日々csv出力して別のシステムのDBにインポートし、活用するような仕組みを作っている。このとき、データの差分のみを抜く方法としては、も...


絵で見てわかるOracleの仕組み (DB Magazine SELECTION)

絵で見てわかるOracleの仕組み (DB Magazine SELECTION)

基礎からのOracle (DVD付) (プログラマの種シリーズ)

基礎からのOracle (DVD付) (プログラマの種シリーズ)

1週間でORACLE MASTERの基礎が学べる本 (徹底攻略)

1週間でORACLE MASTERの基礎が学べる本 (徹底攻略)