DBD::Oracle に激遅のコードが混在している件について
えーっと、ここ数日やっている DBIx::Class 関連なんですが、DBIx::Class::Schema::Loader::Oracle を作ってみたものの、Oracle の場合、なぜか _load_relationships が異常に遅いんですよ。でかなり深追いしてみました。
DBIx::Class::Schema::Loader::Base->_load_relationships あたりが遅いのでメソッドコールを深追い
↓
DBIx::Class::Storage::DBI->columns_info_for の $dbh->column_info そのものが遅いことを突き止める。
my $sth = $dbh->column_info( undef, undef, $table, '%' );
$sth->execute();
while ( my $info = $sth->fetchrow_hashref() ){
・・・
$dbh->column_info つまりは、DBD::Oracle->column_info が原因って事で、まさかの DBD::Oracle を除いてみることに。
DBD::Oracle の解析
DBD::Oracle にデバッグ情報をいれつつ、生成された SQL をみてみた。。。 LIKE 文がすごーく非効率的につかわれてます。まずは結論。修正した Oracle.pm との差分は下記の通り。diff Oracle.pm Oracle.pm.org 371,372c371,372 < if ( defined $SchVal && $SchVal ne '%' ) { < push @Where, ($SchVal =~ /%/) ? "TABLE_SCHEM LIKE '$SchVal' ESCAPE '\\'" : "TABLE_SCHEM = '$SchVal'"; --- > if ( defined $SchVal ) { > push @Where, "TABLE_SCHEM LIKE '$SchVal' ESCAPE '\\'"; 374,375c374,375 < if ( defined $TblVal && $TblVal ne '%' ) { < push @Where, ($TblVal =~ /%/) ? "TABLE_NAME LIKE '$TblVal' ESCAPE '\\'" : "TABLE_NAME = '$TblVal'"; --- > if ( defined $TblVal ) { > push @Where, "TABLE_NAME LIKE '$TblVal' ESCAPE '\\'"; 621,622c621,622 < if( $v && $v ne '%' ) { < $Sql .= ($v =~ /%/) ? " AND $k LIKE ? ESCAPE '\\'\n" : " AND $k = ?\n"; --- > if ( $v ) { > $Sql .= " AND $k LIKE ? ESCAPE '\\'\n"; 627d626 < warn "DBD::Oracle->@BindVals\n$Sql\n";
で、説明。まずオリジナルのコードは条件文をすべて LIKE 文で生成しようとします。たとえば、
他の例もあげますと、
まぁ要するに、インデックスを使わない SQL を生成する作りになっていたので激遅だったわけです。もっとも普段は column_info メソッドなんて使わないから効率を考えて無かったんだろうなぁ〜と思ったり。改造版はかなり高速になります。
それともう一つ猛烈に気になるのが
ってところ。近頃の Oracle はコストベースがデフォルトになってるにも関わらずルールベースで SQL を解析しろと Hint 句を使ってます。うーんうーん・・・微妙だなぁ・・・この Hint 句とっちゃう??
一応、改造した Oracle.pm を公開しておきます。何かのお役に立てればと。。。
ダウンロードはこちら → DBD::Oracle-1.17a.pm
コメントやシェアをお願いします!