2017-01-13 16 views
1

パーティション表で使用する複合ローカル索引索引を構成する最適な方法を理解しようとしています。ここでパーティション表でパーティション索引を使用する

は私の例の表である:

ADDRESS 
id 
street 
city 
state 
tenant 

Addressテーブルには、テナント列時に仕切られたリストです。ほとんどすべてのクエリはクエリ内にテナント列を持ちますので、ここではパーティション間の検索に関心がありません。

select * from address where tenant = 'X' and street = 'Y' and city = 'Z'のようなクエリをできるだけ最適な状態で実行したいと考えています。私には、まず、特定のテナント(パーティション)に限定してから、ローカルパーティションインデックスを使用するのが正しい方法だと思われます。

ここでは、参照テーブルごとに1つのインデックスしか使用できないと考えています。したがって、最も有用な複合ローカルパーティションインデックスを作成したいと考えています。私はそれに街と街を持つ複合インデックスを想像しています。だから私は2つの質問があります:

  1. テナントはインデックスを持っていますか?

  2. 複合インデックスにテナントを含める必要がありますか?

私は完全にパーティションがパーティション索引でどのように機能するかを理解しないと思うと、それは何らかの形であるべき理由の背後にあるいくつかの理解が参考になります。

+0

各パーティションにはいくつの異なるテナントがありますか? LISTパーティションを使用しているので、それほど多くないと仮定します。この場合、テナントのインデックスは無駄です(単一のインデックスまたはコンポジットインデックスの一部であるかどうかに関係なく) –

+0

いいえ、クエリでは1つ以上のインデックスを使用できます。しかし、B *ツリー索引では、これはめったに起きません。しかし、ビットマップインデックスは、デザインによって組み合わせて使用​​されます。 –

+0

@WernfriedDomscheitパーティションごとに1つのテナントが存在するのは、テナントがパーティション化されているためです。いくつかのテナントが約500あるので、いくつかのパーティションがあります。 – AHungerArtist

答えて

1
create index address_city_street_idx on address(city, street) compress 1 local; 

select * from address where tenant = 'X' and street = 'Y' and city = 'Z' 

質問1と2に答えるために:テナントがパーティションであるので、キーはこのインデックスに含まれてはならず、おそらくインデックスに含まれていないはずです。その列はすでにパーティションプルーニングによって関連するセグメントを選択するために使用されています。その作業はコンパイル時または解析時に行われ、事実上フリーです。

テストケースの実行計画は、パーティションプルーニングが行われていることを示しています。操作PARTITION LIST SINGLEKEYのような変数の代わりに列PstartPstopが番号3をリストするという事実は、Oracleがすでに問合せを実行する前にパーティションを決定していることを示しています。オラクルはコンパイル時に無関係なTENANTを即時に破棄しているため、実行時にTENANTを索引でさらに減らす必要はありません。


私のインデックスの提案は、データに関するいくつかの前提に依存します。 CITYもSTREETも、テナントの行を一意に識別するようには聞こえません。 STREETはCITYよりはるかに選択的です。 1つのCITYに複数のSTREETがある場合は、その順序で索引付けし、索引圧縮を使用すると多くのスペースを節約できます。

インデックスが大幅に小さい場合、レベルが低下する可能性があります。つまり、ルックアップに必要なI/Oがわずかに減少します。それが小さくなるとバッファキャッシュに収まる可能性が高くなり、パフォーマンスがさらに向上する可能性があります。

テーブルが大きければ、私はBLEVEL(インデックスレベルの数)が両方で同じになり、両方のインデックスがキャッシュを効果的に使用するには大きすぎると感じています。つまり、(CITY,STREET)(STREET,CITY)の間にパフォーマンスの差異がないことを意味します。しかし、(CITY,STREET)と圧縮では、少なくとも大量の領域を節約できます。

テストケース

私は、あなたは、単に生産の両方のインデックスを作成し、それらを試してみることができないと仮定します。その場合、最初にいくつかのテストを作成したいと思うでしょう。

このテストケースは、私の提案を強く支持するものではありません。これは、より完全なテストケースの出発点にすぎません。大量のデータとより現実的なデータの分布を持つものを作成する必要があります。

--Create sample table. 
create table address 
(
    id number, 
    street varchar2(100), 
    city varchar2(100), 
    state varchar2(100), 
    tenant varchar2(100) 
) partition by list (tenant) 
(
    partition p1 values ('tenant1'), 
    partition p2 values ('tenant2'), 
    partition p3 values ('tenant3'), 
    partition p4 values ('tenant4'), 
    partition p5 values ('tenant5') 
) nologging; 

--Insert 5M rows. 
--Note the assumptions about the selectivity of the street and city 
--are critical to this issue. Adjust the MOD as necessary. 
begin 
    for i in 1 .. 5 loop 
     insert /*+ append */ into address 
     select 
      level, 
      'Fake Street '||mod(level, 10000), 
      'City '||mod(level, 100), 
      'State', 
      'tenant'||i 
     from dual connect by level <= 1000000; 
     commit; 
    end loop; 
end; 
/

--Table uses 282MB. 
select sum(bytes)/1024/1024 mb from dba_segments where segment_name = 'ADDRESS' and owner = user; 

--Create different indexes. 
create index address_city_street_idx on address(city, street) compress 1 local; 
create index address_street_city_idx on address(street, city) local; 

--Gather statistics. 
begin 
    dbms_stats.gather_table_stats(user, 'ADDRESS'); 
end; 
/

--Check execution plan. 
--Oracle by default picks STREET,CITY over CITY,STREET. 
--I'm not sure why. And the cost difference is only 1, so I think things may be different with realistic data. 
explain plan for select * from address where tenant = 'tenant3' and street = 'Fake Street 50' and city = 'City 50'; 
select * from table(dbms_xplan.display); 

/* 
Plan hash value: 2845844304 

-------------------------------------------------------------------------------------------------------------------------------------- 
| Id | Operation         | Name     | Rows | Bytes | Cost (%CPU)| Time  | Pstart| Pstop | 
-------------------------------------------------------------------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT       |       |  1 | 44 |  4 (0)| 00:00:01 |  |  | 
| 1 | PARTITION LIST SINGLE      |       |  1 | 44 |  4 (0)| 00:00:01 |  3 |  3 | 
| 2 | TABLE ACCESS BY LOCAL INDEX ROWID BATCHED| ADDRESS     |  1 | 44 |  4 (0)| 00:00:01 |  3 |  3 | 
|* 3 | INDEX RANGE SCAN      | ADDRESS_STREET_CITY_IDX |  1 |  |  3 (0)| 00:00:01 |  3 |  3 | 
-------------------------------------------------------------------------------------------------------------------------------------- 

Predicate Information (identified by operation id): 
--------------------------------------------------- 

    3 - access("STREET"='Fake Street 50' AND "CITY"='City 50') 
*/ 

--Check execution plan of forced CITY,STREET index. 
--I don't suggest using a hint in the real query, this is just to compare plans. 
explain plan for select /*+ index(address address_city_street_idx) */ * from address where tenant = 'tenant3' and street = 'Fake Street 50' and city = 'City 50'; 
select * from table(dbms_xplan.display); 

/* 
Plan hash value: 1084849450 

-------------------------------------------------------------------------------------------------------------------------------------- 
| Id | Operation         | Name     | Rows | Bytes | Cost (%CPU)| Time  | Pstart| Pstop | 
-------------------------------------------------------------------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT       |       |  1 | 44 |  5 (0)| 00:00:01 |  |  | 
| 1 | PARTITION LIST SINGLE      |       |  1 | 44 |  5 (0)| 00:00:01 |  3 |  3 | 
| 2 | TABLE ACCESS BY LOCAL INDEX ROWID BATCHED| ADDRESS     |  1 | 44 |  5 (0)| 00:00:01 |  3 |  3 | 
|* 3 | INDEX RANGE SCAN      | ADDRESS_CITY_STREET_IDX |  1 |  |  3 (0)| 00:00:01 |  3 |  3 | 
-------------------------------------------------------------------------------------------------------------------------------------- 

Predicate Information (identified by operation id): 
--------------------------------------------------- 

    3 - access("CITY"='City 50' AND "STREET"='Fake Street 50') 
*/ 

--Both indexes have BLEVEL=2. 
select * 
from dba_indexes 
where index_name in ('ADDRESS_CITY_STREET_IDX', 'ADDRESS_STREET_CITY_IDX'); 

--CITY,STREET = 160MB, STREET,CITY=200MB. 
--You can see the difference already. It may get larger with different data distribution. 
--And it may get larger with more data, as it may compress better with more repetition. 
select segment_name, sum(bytes)/1024/1024 mb 
from dba_segments 
where segment_name in ('ADDRESS_CITY_STREET_IDX', 'ADDRESS_STREET_CITY_IDX') 
group by segment_name; 
+0

質問1と2の答えは、Oracleソースを引用することは可能でしょうか?私はOracleのマニュアルを見て、不確かなままにしていたので、おそらく私が間違った場所を探していたか、理解していなかったでしょう。それは既に良い答えにいくつかの権限を追加するだけです。 – AHungerArtist

+0

@AHungerArtist私は答えにいくつかの情報を追加しました。 –

+0

ありがとうございました。 – AHungerArtist

1

インデックスが一意の場合は、ローカルにするためにTENANTを含める必要があります。 LIST/RANGEパーティションの場合は、パフォーマンスが向上しないため、一意でない場合は含めないでください。 1つのパーティションに多数の異なる値を持つハッシュ・パーティションである場合は、そのパーティションを含めることを検討できます。

UPD:ただし、使用しているパーティションの種類(「静的」または「動的」)によって異なります。 「静的」とは、すべてのパーティションがcreate tableステートメントで一度定義され、アプリケーションの実行中に変更されないままである場合です。 「動的」は、アプリケーションがパーティションを追加/変更するときです(毎日のプロセスでは、すべてのテーブルなどの毎日のリストパーティションが追加されます)。

"ダイナミック"パーティションのグローバルインデックスを避ける必要があります。この場合、新しいパーティションを追加するたびに無効になります。静的なオプションの場合、時にはすべてのパーティションをスキャンする必要がある場合は、グローバルインデックスを使用しても問題ありません。

私はインデックスがリストテナントに-partitionedされているテーブルを指定して、このクエリのための理想的であると信じている
+0

しかし、これで一意でないインデックスを最大限に活用するには、私のwhere句に常にパーティションキーを含める必要がありますか? – AHungerArtist

+0

@AHungerArtistパーティションプルーニングを利用するには、パーティションキーを含める必要があります。 SQLモニタレポートを実行して、実行計画を確認することができます。または、プランだけを表示するには、クエリを実行した後にこれを実行します。select * from table(dbms_xplan。 – BobC

+0

@AHungerArtist - パーティションテーブルのwhere句は、インデックスに関係なく常に部分列でフィルタを含める必要があります。私は1つのことに言及するのを忘れていました - 私の答えを更新させてください – Rusty

関連する問題