Oracleで値の偏りが強い列を索引項目とする場合

Oracleで値が偏る項目を索引付け、検索する場合には、統計情報の採り方に着目すること。

経緯

Oracle DBMSを対象とした、とあるテーブルメンテ(古いレコードを消すだけ)機能で、母数1000万件(うち削除対象が1000件)程度の大量データでテストすると性能が出ないとのことで「鋼のオラクルマスター」の称号を持つ私が調べてみた。
作業にあたってDBA権限をもつユーザでログイン(ちなみに私は本来この環境でDBA権限など持つ立場ではない*1が、幸いデフォルトのDBA権限ユーザとパスワードが割れた)。

アクセスパスを調べると、削除対象を絞り込むSELECT文で索引が上手く適用されない。疑わしきは統計情報。DBA_INDEXESを調べるとLAST_ANALYZED列が空白なので、統計情報を採っていないのだと判明。

そこで表と索引の統計情報を採ってみたが、結果、問題のSELECT文に関しては変わらず。

次にデータの分布を疑うと、テスト担当者は、検索に用いる索引項目の値を、処理対象1種類(件数1000)と非処理対象(件数1000万)の合わせて2種類にのみ分けてテストデータを用意していた。この索引項目値の強い偏りが、最適な実行計画を導出できない原因であった。

値の偏る項目への索引付けについて

CBO*2が表と索引について持つ情報は、以下の通り。

  • 表の全件数
  • 項目の最大値・最小値
  • 項目の一意な値の数 (ALL_TAB_COLUMNS等のDISTINCT_KEYS)

ただし、項目の値は一様に分布するものと仮定される。
そのために、値の分布に偏りが顕著な項目に索引付けした場合には、実状に即した最適なアクセスパスが現れないことがある。

こういった項目に索引付けする場合は、列統計情報を取得し、ヒストグラムを生成する必要がある。

具体的には、DBMS_STATS.GATHER_TABLE_STATSにmethod_optパラメータを指定する。

Oracle9i PL/SQL パッケージ・プロシージャおよびタイプ・リファレンス(PDF)より

method_opt 次を受け入れます。
FOR ALL [INDEXED | HIDDEN] COLUMNS [size_clause]
FOR COLUMNS [size clause] column|attribute [size_
clause] [,column|attribute [size_clause]...]
size_clause は次のように定義されます。
size_clause := SIZE {integer | REPEAT | AUTO |
SKEWONLY}
integer −ヒストグラムバケット数。 1 〜 254 の範囲です。
REPEAT −すでにヒストグラムがある列に関してのみ、ヒストグ
ラムを収集します。
AUTO −列のデータ配分とワークロードに基づいて、ヒストグラ
ムを収集する列が判断されます。
SKEWONLY −列のデータ配分に基づいて、ヒストグラムを収集す
る列が判断されます。

このパラメータのデフォルト値は'FOR ALL COLUMNS SIZE 1'つまり内部的にはヒストグラムバケット1で生成されている。ここにAUTOなりSKEWONLYなり指定すれば、適切なバケット数でヒストグラムが作成されるはずだ。

*1:このOracle環境は他部署の持ち物を間借りしている。本来我々のシステムは別のDBMS向け。Oracleも対応するが「一応動く」レベルで良く、自前でOracle環境を持たない。

*2:コストベース・オプティマイザ表や索引の統計情報をもとに、コスト(読込みブロック数、I/OやCPU使用量、実行時間など)が最も小さい実行計画を選択する機能。