なぜオラクルは「完璧な」インデックスを無視するのでしょうか?
-
20-09-2019 - |
質問
私はこのテーブルを持っています:
create table demo (
key number(10) not null,
type varchar2(3) not null,
state varchar2(16) not null,
... lots more columns ...
)
そしてこのインデックス:
create index demo_x04 on demo(key, type, state);
このクエリを実行したとき
select * from demo where key = 1 and type = '003' and state = 'NEW'
EXPLAIN PLAN
完全なテーブルスキャンを行うことを示しています。それで、私はインデックスを落とし、再びそれを作成しました。 EXPLAIN PLAN
まだ完全なテーブルスキャンと言っています。それはどうでしょうか?
いくつかの背景:これは履歴データですので、何が起こるかが私が状態で行を調べることです CLEARED
状態で新しい行を挿入します NEW
(さらに、古い行からいくつかの値をコピーします)。その後、古い行が更新されます USED
. 。そのため、テーブルは常に成長します。私が気づいたのは、インデックスのカーディナリティが0であるということです(私には何千もの異なる値があるという事実にもかかわらず)。再作成後、カーディナリティは成長しましたが、CBOはインデックスの方が好きではありませんでした。
翌朝、オラクルは突然インデックスが好きで(おそらくその上で寝ていた)、それを使い始めましたが、長くはありませんでした。しばらくすると、処理は50行/sから3行/sに低下し、再び「フルテーブルスキャン」を見ました。何が起こっている?
私の場合、約100万行を処理する必要があります。 CAのバッチの変更を犯します。 50.インデックスなどを更新/再起動するためにコミットした後に実行する必要があるコマンドはありますか?
私はOracle 10Gにいます。
編集]このテーブルには969'491の個別のキー、3つのタイプと3つの状態があります。
解決
インデックスのヒントを指定するとどうなりますか?これを試して:
SELECT /*+ INDEX (demo demo_x04) */ *
FROM demo
WHERE key = 1
AND type = '003'
AND state = 'NEW';
一晩で起こったことは、テーブルが分析されたことだったようです。次に、テーブルに対して処理を実行すると、Oracleのテーブルの統計が再び古くなって、Optimizerがインデックスの使用を停止するために、インデックスの十分なインデックスが更新されました。
ヒントを追加して、説明計画が別の計画を提供するかどうかを確認し、クエリのパフォーマンスが向上します。
ああ、テーブルの分析に関するトニーの答えは一般的な良い実践ですが、10gではデータベースはその点で自己メンテナンスを行うことについてかなり良いです。プロセスが多くの更新を行っている場合、インデックスはすばやく古くなる可能性があります。実行を実行すると、プロセスが溝に進み始めるとしばらく状況が改善されると、これが問題であることがわかります。
テーブルの統計を更新するには、を使用してください dmbs_stats.gather_table_stats パッケージ。
例えば:
exec dbms_stats.gather_table_stats( 'the owner'、 'demo');
他のヒント
テーブルは最近分析されましたか? Oracleがそれが非常に小さいと考えている場合、インデックスの使用さえ考慮しないかもしれません。
これを試して:
select last_analyzed, num_rows
from user_tables
where table_name = 'DEMO';
Num_Rowsは、Oracleがテーブルに含まれていると思う行の数を教えてくれます。
「翌朝、オラクルは突然インデックスが好きだった(おそらくその上で眠った)」と思われるでしょう。おそらくDBMS_STATSが一晩走っています。
一般的に、インデックス上の完全なテーブルスキャンの3つの理由の1つが表示されます。 1つ目は、オプティマイザーがテーブルが空であるか、少なくとも非常に小さいと考えていることです。これが最初の問題だったと思います。その場合、インデックスを使用するのではなく、ほんの一握りのブロックで構成されるテーブルを完全にスキャンするのが速くなります。
2つ目は、クエリがインデックスを実際に使用できないような場合です。
"select * from demo where key = 1 and type = '003' and state = 'NEW'"
実際にクエリでハードコーディングされたリテラルを使用していますか?そうでない場合、可変データ型が間違っている可能性があります(例:キーは文字です)。そのためには、数値キーを比較のために文字に変換する必要があります。これにより、インデックスがほとんど役に立たなくなります。
3番目の理由は、クエリがテーブル内の行の大部分を処理すると考える場合です。タイプと状態はかなり低いカーディナリティのようです。おそらく、特定の「キー」値を多数持っていますか?
あなたが説明する処理に関するコメント:断続的なコミットで列ごとの処理を行っているように聞こえます。できればこれを再考することをお勧めします。更新/挿入メカニズムはマージステートメントに変換される可能性があり、データセット全体を1つのコミットで1つのステートメントで処理できます。これはほぼ確実に高速であり、現在の方法よりも少ないリソースを使用します。
列キーの値は常に1ですか?もしそうなら、各行を調べる必要があるため、インデックスをコンサルティングすることがクエリを最適化するかどうかはわかりません。その場合、キー列なしでインデックスを宣言します。あなたも試すことができます:
select key, type, state from demo where key = 1 and type = '003' and state = 'NEW'
(私の推測が正しい場合は)まだ各行を見る必要がありますが、結果セットのすべての列がカバーされるため、インデックスに移動する可能性があります。
私はあなたの声明に基づいて、インデックスがカーディナリティ0を示していると推測しているだけです。