セッション管理に向いているデータベースは 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 を使うという手もあるかと。