ROWID スキャンへの過剰な期待

オラクルのSQLパフォーマンスチューニングにおいて ROWID スキャンによるアクセスが、あらゆるケースで最適なアクセスパスであると思われていることがある。 これは正しい情報ではない。というよりもマニュアルの表現を一部を省いて覚えられている事が多いので、 大きな誤解である。ということにしておこうと思う。

ROWID=最速である。この拡大解釈によってパフォーマンスの向上に行き詰った状態から脱出しようとして、 無理矢理に ROWID によるアクセスパスになるように SQL を書き換えてしまい、なんで速くならないのだろうか…と 頭を抱える事や作業以前の予想とは反対に遅くなることもある。

Oracle 11g で追加された DBMS_PARALLEL_EXECUTE パッケージにて説明のない ROWID (dda) ヒントが登場しており、ROWID の集合への効率的な特殊なアクセスパスができたかもしれない。

ROWID スキャン とは

ROWID スキャンとは、「単一行を取得する最も高速な方法」、マニュアルにもそのように書かれている。
この文で単一行という単語はマニュアルでボールド体にしておいた方が良いと思える位に、 誤解を解消する重要なキーワードである。

ROWID スキャンより効率的なアクセスパス

「単一行を取得する場合に最速」ということは、複数行の取得において ROWID より効率よくデータを検索することが できるアクセスパスが存在しているということになる。一体それはどのようなアクセスなのか?

それは「インデックススキャン」による アクセスパス である。以後、話が少々紛らわしくなるが、すべてのケースで効率が良いわけではない。 効率が良いのはインデックスだけでデータの取得が完了する場合である。 これは NOT NULL 項目を含む複合インデックスや複合主キーなどにおいて発生しやすい。(※1)

(※1) これ以外にも ROWID より効率的なケースがあるだろう。これらの判定は CBO が行なってくれる。

例えば ブロックサイズが 2K バイトの表領域の表 (table:(key1,key2,col,..)) に主キーが定義されており 2 つの NUMBER 型 (key1, key2) によって構成されているとする。 この表には 1 レコード が 1500 バイトのデータ(1レコード、1ブロック)が大量に格納されており、 大規模表 に位置付けられた表ということにする。

この大規模表 (table) において ROWID スキャンとインデックススキャンの 2 つの SQL を比較する。 もちろん比較する 2 つの SQL は結果が同一であることが前提である。

(R1) SELECT col FROM table WHERE ROWID = 'OOOOOOFFFBBBBBBRRR'  ;
(I1) SELECT col FROM table WHERE key1= 1 AND key2 = 1;
--          ^^^
(R2) SELECT col FROM table WHERE ROWID IN (a,b,c,...z);
(I2) SELECT col FROM table WHERE key1 =1 AND key2 BETWEEN 1 AND 26 ;
--
(R3) SELECT key2 FROM table WHERE ROWID IN (a,b,c,........zzz);
(I3) SELECT key2 FROM table WHERE key1 = 1 ;
--          ^^^^
(R4) SELECT key2 FROM table WHERE ROWID = 'OOOOOOFFFBBBBBBRRR';
(I4) SELECT key2 FROM table WHERE key1 = 1 AND key2 = 1;

上記の例の (R1,I1) 、 (R2,I2) においては ROWID が高速である。 これは指定条件によってインデックス内を探索して ROWID を取り出す。さらに ROWID から表にアクセスし col を取り出しているため ROWID によるアクセスパスが圧勝する。

しかし (R3,I3) の場合にはちょっと様子が変わってくる。(同一の 1000 レコードを検索する SQL と仮定する)
(R3) は 1000 個の ROWID を元に 2K バイトのブロックを 1000 回(※2)を読み出して key2 を戻す。
(I3) は インデックス内の複数のブロックを探索して key1 = 1 が格納されている位置にたどり着く。 そこに key1, ROWID とともに、そこから連続して格納されている key2 を戻すことで目的は達成する。
実際に表データが格納されているデータブロックにアクセスする必要がない。

(※2) CBO の環境で数百レコードの表で実験してみたところ、テーブル・フルスキャンが 実行計画 されてしまうのかもしれないと思ったが ROWID は読み込む対象が存在しないから… ROWID スキャンになるようです。 FULL ヒントを使用すると全表スキャンのアクセスパスになって consitent get が減少した。

では (R4,I4:I1 とは取得カラムだけの違い) では、どうか?
これは key1 = 1 AND key2 = 1 を見つけ出すための作業量次第であるが ROWID が圧倒的優勢。 それは key1 = 1 AND key2 = 1 を探索するためにはインデックスを数ブロックを読み出し、さらに条件の評価をする必要があるためである。
ただ、表データよりも索引のデータの方がキャッシュに保存される可能性が高いため完勝でなく優勢としておく。



関連事項

ROWID スキャンより高速なアクセスパスの関連トピックス

日本オラクル
■ 日本オラクル 株式会社
■ オラクルマスター資格 (オラクルマスターとは
■ オラクルサポートセンター