セッション管理に向いているデータベースは MySQL ? Oracle ?
Catalyst-Plugin-Session-Store-DBIC とか検証してます。で前から気になってはいたのですが、Perl 界ではセッション管理するモジュールといえばほぼ全て MySQL が前提っぽい作りになってると思います。でも業務で使っているデータベースは Oracle でして、う〜ん・・・どうすっべかなぁ〜と思ってました。
で、試しに Oracle でセッション管理するためのテーブルをつくってみました。Oracle は VACHAR2 型とかは 4000 文字までしか扱えないので、セッションデータとして使うにはちょっと物足りないデータ型。CLOB 型を使えば解決できるんですが、DBI ベースにいろいろと指定してあげないとダメ。しかも多分遅い。遅いと推測していたから、今まで LOB や LONG 型はあえて使ってきませんでした。
session 格納に使うテーブルの構造はこんな感じで定義。あくまで試験用のテーブル構造。
-- oracle CREATE TABLE sessions ( id varchar(72) NOT NULL, session_data nclob, expires integer, PRIMARY KEY (id) ); -- mysql CREATE TABLE sessions ( id varchar(72) NOT NULL, a_session longblob, timestamp timestamp, PRIMARY KEY (id) );
いきなり結論。Oracle 10g でも遅くて使い物になりませんでした
insert で約40倍の遅さ。select で約6倍の遅さという結果になりました。やっぱ DBD::Oracle で LOB 型は使うとダメっぽいですね。__追記(50K 程度の大きさの session データで再検証)__
insert で約30倍の遅さ。select で約9倍の遅さという結果になりました。ほぼ同じですね。
ベンチマークスクリプトはこんな感じ。1万件のデータを insert した後に100回 select をするってやつ。セッションデータとして CGI->new をシリアライズしたものをぶち込む。実際には GET/POST のデータが存在しないので非常に小さなセッションデータとなります。
use MIME::Base64;
use Storable qw/nfreeze thaw/;
use DBI;
use DBD::Oracle qw(:ora_types);
use CGI;
my $oracle = DBI->connect('dbi:Oracle:testdb', 'test', 'test123');
my $mysql = DBI->connect('dbi:mysql:testdb', 'test', 'test123');
$oracle->{LongReadLen} = 102400;
$oracle->{LongTruncOk} = 1;
&bench_insert($mysql, 'mysql');
&bench_insert($oracle,'');
&bench_select($mysql, {});
&bench_select($oracle, {ora_auto_lob => 0});
sub bench_select {
my $dbh = shift;
my $args= shift;
my $loop = 100;
$begin = time;
foreach(1..$loop) {
my $sth = $dbh->prepare("select * from sessions", $args);
$sth->execute;
while(my $row = $sth->fetchrow_hashref) { 1; }
$sth->finish();
}
$end = time;
print "TIME=".($end-$begin)."\n";
}
sub bench_insert {
my $dbh = shift;
my $args= shift;
my $loop = 10000;
$begin = time;
my $sql = ($args eq 'mysql') ?
'insert into sessions (id,a_session,timestamp) values (?,?,?)' :
'insert into sessions (id,session_data,expires) values (?,?,?)';
my $data = encode_base64(nfreeze(CGI->new));
foreach(1..$loop) {
my $sth = $dbh->prepare($sql);
$sth->execute($_,$data,$_);
$sth->finish();
}
$end = time;
print "TIME=".($end-$begin)."\n";
}
で結果はこんな感じ。
TIME=2 TIME=80 (※メモリが潤沢にあるサーバだと =24 になった。うんうん。) TIME=28 TIME=165 (※メモリが潤沢にあるサーバだと =311 になった。あれ?) __追記(50K 程度の大きさの session データで再検証)__ TIME=3 TIME=87 TIME=42 TIME=365
あぁ・・・オラクル遅ぇ〜・・・こんなに差が開くとは思ってませんでした。。。insert がこんなに遅いんじゃぁ〜セッション管理に使うとサーバのレスポンスがすげぇ〜遅くなりそうな予感満点ですなぁ・・・
さて、最後に DBD::Oracle で LOB 型を使うための方法について説明を。ってか、「DBD::Oracle - DBIモジュールのためのOracleデータベース・ドライバ」をみればわかります。ついでなので必要部分を引用。
LOBを挿入または更新するとき、透過的にしている裏で、いくつかの大きな 魔法が行われなければなりません。それらが書き込みできるようになる前に、基本的にドライバは新しく挿入された'LOB Locator'を再びフェッチしなければいけません。しかしながら、これは機能し、私はそれをできる限り速くしています(挿入または更新の後の最初にサーバとの間をたった1回、余分に往復します)。さしあたって、1行だけのLOBの更新もサポートしています。またPL/SQLブロックへ複数LOBを渡すことは機能していません。
大きなLOBの挿入または更新をするには、前もってDBD::OracleがそれがLOB型であることを知っていなければなりません。そこで以下のように言わなければなりません:
$sth->bind_param($field_num, $lob_value, { ora_type => ORA_CLOB });
定数ORA_CLOBとORA_BLOBは以下のようにしてインポートされます:
use DBD::Oracle qw(:ora_types);
または単純に対応する整数値を使います(112 と 113)。
Oracle7とOracle8の両方でスクリプトを動かせるようにするためには、 Oracle7 DBD::Oracleはエラーなしに、LOB ora_typeをLOGとして扱います。そこで全てのコード、今もっているコードは以下のようになるかもしれません
$sth->bind_param($idx, $value, { ora_type => 8 });
この8(LONG型)をORA_CLOBやORA_BLOB(112や113)に変更することができます。
さらにもう一点:LOBの挿入、更新について、DBD::Oracleはパラメータが、どのテーブルのフィールドに関係しているかを伝えることが可能でなければなりません。それだけで解決できる場合であれば、それだけで機能します。しかしテーブルに同じ型のLOBフィールドが複数あるならば、各LOBパラメータがどのフィールドに関連するのかを知らせなければなりません:
$sth->bind_param($idx, $value, { ora_type=>ORA_CLOB, ora_field=>'foo' });
今のところ、DBD::Oracleを使って小さな塊でLOBを直接書き込む方法はありません。しかし、PL/SQLでDBMS_LOB.WRITEAPPENDを使って可能です(しかし効率的ではありません)。
LOBをINSERTするためには、UPDATE権限が必要です。
prepare()で/ora_auto_lobが0であれば、LOBロケータを取り出し、 Oracle::OCIを使ってあなた自身で全てのことを行うことが出来ます。


コメントやシェアをお願いします!
drk
ニック さん>
当方 Oracle マスタープラチナ保有者ですが、資格はあまり関係ないかと思います。仕事上、Oracle も MySQL もかなり使い込んでいる経験上で記事を書いていますが、最終的にご自分で試験して確認頂くのが宜しいかと。テスト環境でも性能は変化しますので。もっとも Oracle と MySQL では使いどころが違いますし。
この記事に限らず、情報というものはひとつの指標としてとらえ、最後は自分で確認するのが宜しいかと。
ニック
素人なのでわからないのですが、なぜ Oracle では遅くなるんですか?
Oracle 最適の方法で試してもだめなんですか?
MySQL と Oracle の使い方や考え方は全く異なると聞いたことがあります。
あなたは Oracle についてどれくらい詳しいですか?
Oracle マスタープラチナの方が言っていることであれば信憑性があるのですが、MySQL しか知らない方が書いた記事がインターネットに乗っていると、素人にとっては混乱させられます。
drk
tokuhiromさん>実はちょうど memcached を実験中でした(w
tokuhirom
memcached を使うという手もあるかと。