Oracleは長いキーに一意のインデックスを使用していません


16

テストデータベースに25万行のテーブルがあります。(本番環境には数億個あります。同じ問題があります。)テーブルには、nvarchar2(50)文字列識別子があり、nullではなく、一意のインデックスが付いています(PKではありません)。

識別子は、テストデータベースに8つの異なる値(および運用中に約1000)を持つ最初の部分、@記号、最後に1〜​​6桁の数字で構成されます。たとえば、「ABCD_BGX1741F_2006_13_20110808.xml @」で始まる5万行があり、その後に5万の異なる数字が続く場合があります。

識別子に基づいて単一の行を照会すると、カーディナリティは1と推定され、コストは非常に低く、正常に機能します。IN式またはOR式で複数の識別子を使用して複数の行を照会すると、インデックスの推定が完全に間違っているため、テーブル全体のスキャンが使用されます。ヒントを使用してインデックスを強制すると、非常に高速になります。実際には、テーブル全体のスキャンが1桁遅く実行されます(運用環境でははるかに遅くなります)。それはオプティマイザーの問題です。

テストとして、まったく同じDDLとまったく同じコンテンツを使用してテーブル(同じスキーマ+テーブルスペース)を複製しました。適切な測定のために最初のテーブルに一意のインデックスを再作成し、クローンテーブルにまったく同じインデックスを作成しました。私はDBMS_STATS.GATHER_SCHEMA_STATS('schemaname',estimate_percent=>100,cascade=>true);。インデックス名が連続していることもわかります。したがって、2つのテーブルの唯一の違いは、最初のテーブルが長期間にわたってランダムな順序でロードされ、ブロックがディスクに(他のいくつかの大きなテーブルと一緒にテーブルスペースで)散らばっていることです。 INSERT-SELECT。それ以外、違いは想像できません。(元のテーブルは最後の大規模な削除以降縮小されており、その後の単一の削除はありません。)

病気のテーブルとクローンテーブルのクエリプランを次に示します(黒いブラシの下の文字列は、画像全体で同じであり、灰色のブラシの下でも同じです)

クエリプラン

(この例では、黒のブラシをかけられた識別子で始まる1867行があります。2行クエリは1867 * 2のカーディナリティを生成し、3行クエリは1867 * 3のカーディナリティを生成します。偶然ですが、Oracleは識別子の終わりを気にしていないようです)

この動作の原因は何ですか?本番環境でテーブルを再作成するのは明らかに高価です。

USER_TABLES:http: //i.stack.imgur.com/nDWze.jpg USER_INDEXES:http : //i.stack.imgur.com/DG9um.jpg スキーマとテーブルスペース名のみを変更しました。テーブルとインデックスの名前は、クエリプランのスクリーンショットと同じであることがわかります。

回答:


7

(これは、ヒストグラムが異なる理由に関する他の質問に答えます。)

ヒストグラムは、デフォルトでは、列のスキュー、関連する述語で列が使用されたかどうかに基づいて作成されます。DDLとデータをコピーするだけでは不十分です。ワークロード情報も重要です。

パフォーマンスチューニングガイドによると:

テーブルを削除すると、自動ヒストグラム収集機能で使用されるワークロード情報と、RESTORE _ * _ STATSプロシージャで使用される保存された統計履歴が失われます。このデータがないと、これらの機能は適切に機能しません。

たとえば、データが歪んでいるがヒストグラムはないテーブルは次のとおりです。

drop table test1;
create table test1(a date);
insert into test1 select date '2000-01-01'+level from dual connect by level <= 10;
insert into test1 select date '2000-01-01' from dual connect by level <= 1000;
begin
    dbms_stats.gather_table_stats(user, 'TEST1');
end;
/
select histogram from user_tab_columns where table_name = 'TEST1';

HISTOGRAM
---------
NONE

同じことを実行しますが、統計が収集される前にクエリを実行すると、ヒストグラムが生成されます。

drop table test1;
create table test1(a date);
insert into test1 select date '2000-01-01'+level from dual connect by level <= 10;
insert into test1 select date '2000-01-01' from dual connect by level <= 1000;
select count(*) from test1 where a = sysdate; --Only new line
begin
    dbms_stats.gather_table_stats(user, 'TEST1');
end;
/
select histogram from user_tab_columns where table_name = 'TEST1';

HISTOGRAM
---------
FREQUENCY

2
見事にシンプルな例。CBOが1を仮定するだけでなく、一意のスキャンでカーディナリティの推定にヒストグラムを使用した理由をご存知ですか?
ジャックダグラス

ありがとう!私のブログで私の種類のデータとクエリで完全な再現を行いました:joco.name/2014/01/05/…– fejesjoco 14
1

@Jack怠inessだと思う。Oracleエンジニアは、一意の索引の統計には行と同じ数の個別の値があるため、1カーディナリティの仮定は固定されておらず、他の場合と同様に統計から単純に使用されると考えていたはずです。また、一般的なケースとして、ヒストグラムは単純な統計に勝ります。私のケースは長いキーだけのために非常に特別なようですが、これは他の点ではかなりうまくいくと思います。
fejesjoco 14年

@fejesjoco JLの説明は、ヒストグラムが単一のルックアップ(なしin)の場合の一般的な統計よりも優れているため、より可能性が高いと思いますか?CBOはカーディナリティー1を仮定しますが、これは非常に単純な場合に限られます。大きなものを使用して全体をUNION ALL回避できると思いますが、それをしない他の理由があるかもしれません。JLはリンクされたブログ投稿で他の可能な回避策に言及しています。
ジャックダグラス14年

1
考慮すべきもう1つの小さな謎-そもそもこのヒストグラムはどのように作成されたのでしょうか?Oracleは、列に重複がある場合にのみ列が歪んでいると見なすようです。誰かがこのヒストグラムを意図的に作成しましたか(そうではありません)、または誰かが非推奨の統計を収集しましたmethod_opt=>'for all indexed columns'か?
ジョンヘラー14年

8

私は解決策を見つけました!それはとても美しく、実際に私はオラクルについてたくさん学びました。

一言で言えば、ヒストグラムです。

OracleのCBOがどのように機能するかについて多くのことを読み始め、ヒストグラムにつまずきました。私は完全に理解していなかったので、USER_HISTOGRAMSテーブルとvoiláを調べました。病気のテーブルにはいくつかの行があり、クローン化されたテーブルには事実上何もありませんでした。病気のテーブルの場合、8つの異なる識別子開始部分ごとに1つの行がありました。そして、これが鍵です:@記号の前の32文字で切り捨てられました。前にも言ったように、キーの最初の部分は非常に繰り返しが多く、@記号の後に異なるものになります。

ヒストグラムは、特定の値に対して一意のインデックスの基数が常に0または1であるという単純な事実よりも強力であると思われます。2行以上のクエリを実行しているときに、Oracleはヒストグラムを調べ、その識別子開始部分に何万もの値がある可能性があると考え、CBOを破棄しました。

古いテーブルのその列のヒストグラムを削除すると、問題はなくなりました!

詳細情報:https : //blogs.oracle.com/optimizer/entry/how_do_i_drop_an_existing_histogram_on_a_column_and_stop_the_auto_stats_gathering_job_from_creating


2
私は私達のチャットルーム:)でいることを述べたchat.stackexchange.com/transcript/message/12987649#12987649
Philᵀᴹ

私はそれを見ませんでした:)。だから唯一の奇妙なことは、クローンではなく最初のテーブルにヒストグラムがあった理由です。私は、gather_schema_statsがすべてを更新したと思いました。
fejesjoco

6

私はこれについてジョナサン・ルイスにメールを送り、非常に役立つ返信を得ました:

計算の奇妙さは、文字ベースのヒストグラムの制限の結果です。特に以下を参照してください。

http://jonathanlewis.wordpress.com/2010/10/13/frequency-histogram-5/ http://jonathanlewis.wordpress.com/2010/10/19/frequency-histograms-6/

この例を見ると、クエリは単一の行ではなくINリストを対象としているため、私の最初の推測は、オプティマイザが複数の行の選択性を計算するための特別なケースのコードではなく、一般的な戦略を使用したことです主キーのINリスト。彼らがこのケースを認識することはそれほど難しいことではないと思いますが、開発者はおそらく努力する価値があるとは考えていません。

彼がリンクしているブログ投稿を読むことを強くお勧めします。彼らはあなたが走っているヒストグラムの制限を詳細に記述しています、例えば:

結論:頻度ヒストグラムの適切な候補である列に非常に長い類似の文字列がある場合(非常に記述的なステータス列など)、非常にまれな値が非常に人気のあるものと同じに見える場合、問題があります最初の32文字までの値。唯一の解決策は、正当な値のリストを変更することである場合があります(ただし、仮想列または関数ベースのインデックスを含むさまざまな戦略は問題を回避できます)。


悲しいことに、ヒストグラムはあまり知られていない機能のようです。SQL開発者にとっては深すぎて、ほとんどの場合は機能しているだけだと思いますが、多くのリソースがあることを知っているのは良いことです。正しい場所:)。Oracleが32バイトを削減し、それに基づいて悲惨な決定を下すのはかなり悪いことです。幸いなことに、私は微調整する必要はありません。ヒストグラムをドロップすることは完璧なソリューションです。キー値は一意であり、私は常に一度に20個の値を探しますが、インデックスでのみ正常に機能し、決定論的です。しかし、次回は長いキーを使用しません。それは確かです。
fejesjoco

ヒストグラムはDBAの間でよく知られています;)より深いことを学ぶことに熱心であり、JLの本を読むべきだと本当に思うのが大好きです。CBOは一般に素晴らしい仕事をします。調査が必要なエッジケースは常に存在しますが、カットオフがなくても、推定値は常に単なる推定値であることに留意する価値があります。
ジャックダグラス14年

1
通常の統計ジョブ(クリーンインストールでデフォルト実行されるOracleなど)を実行すると、ヒストグラムが再び表示される場合があります。これを防ぐ方法を検討する必要がある場合があります(LOCK_TABLE_STATSなど)
Jack Douglas

回答の中でブログ投稿について言及しましたが、列のヒストグラムを防ぐ方法についての指示があります。
fejesjoco

1
@Jack Douglas、J。ルイスを巻き込んで報告してくれてありがとう!
ディミトレラドゥロフ14年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.