この章の内容は次のとおりです。
関連項目: 索引付けの概要は、『Oracle Database概要』を参照してください。 |
ビットマップ索引は、データ・ウェアハウス環境で広く使用されています。通常、データ・ウェアハウス環境では、大量のデータおよび非定型の問合せがありますが、DMLトランザクションが同時に発生することは稀です。このようなアプリケーションでは、ビットマップ索引による次のメリットがあります。
大規模な非定型問合せに対する応答時間が削減されます。
他の索引付けテクニックと比較すると、領域の使用量が少なくて済みます。
比較的CPUの数が少ないハードウェアまたはメモリー量が少ないハードウェアでも、大幅にパフォーマンスが向上します。
パラレルDMLおよびロード中に、効率的にメンテナンスができます。
大規模な表を従来のBツリー索引で完全に索引付けすると、索引が、表にあるデータの数倍の大きさになる場合があるため、ディスク領域の点で非常にコストが高くなります。通常、ビットマップ索引のサイズは、表内の索引付けされたデータの何分の1かの大きさで済みます。
索引は、指定したキー値を含む表の行へのポインタを保有します。通常の索引には、そのキー値がある行に対応する各キーのROWIDのリストが格納されます。ビットマップ索引では、各キー値のビットマップが、ROWIDのリストのかわりに使用されます。
ビットマップの各ビットは、ROWIDに対応します。ビットが設定されると、対応するROWIDを持つ行に、キー値が含まれることを意味します。マッピング機能によってビットの位置が実際のROWIDに変換されるため、ビットマップ索引は、通常の索引と同じ機能を提供します。ビットマップ索引は、ビットマップを圧縮して格納します。個別キー値の数が少ない場合は、ビットマップ索引の圧縮率が高くなり、Bツリー索引に対する領域節約の面での優位性がさらに高くなります。
ビットマップ索引は、WHERE
句に複数の条件が含まれる問合せに対して最も効率的です。すべての条件ではなく一部の条件のみを満たす行は、表自体がアクセスされる前に除外されます。これによって、応答時間が大幅に削減されます。どの索引を作成するべきか判断できない場合は、SQLアクセス・アドバイザを使用すると、作成するべき索引についてのリコメンデーションが生成されます。ビットマップ索引のビットマップは瞬時に結合されるので、通常は単一列のビットマップ索引を使用するのが最適です。
ビットマップ索引を作成するときは、NOLOGGING
およびCOMPUTE
STATISTICS
を使用する必要があります。また、ビットマップ索引は、通常、メンテナンスを行うよりも削除して再作成するほうが簡単であることに注意してください。
ビットマップ索引は、ユーザーがデータの更新ではなく、データの問合せを行うデータ・ウェアハウス・アプリケーションに使用します。この種の索引は、データを変更する同時トランザクションの数が多いOLTPアプリケーションには適していません。
パラレル問合せおよびパラレルDMLは、ビットマップ索引でも動作します。ビットマップ索引では、索引のパラレル作成、連結索引もサポートされます。
ビットマップ索引は、表の行数に対する個別値の数の比率が小さい列に対して最も効果的です。この比率をカーディナリティ度と呼びます。2つの個別値(男性および女性)のみを持つ性別の列は、ビットマップ索引に最適です。ただし、データ・ウェアハウス管理者は、カーディナリティが高い列にビットマップ索引を作成する場合もあります。
たとえば、行が100万ある表では、10,000の個別値を持つ列がビットマップ索引の候補になります。この列のビットマップ索引は、特に、この列が他の索引付けされた列と連結して頻繁に問い合せられる場合に、Bツリー索引よりパフォーマンスが高くなります。実際、典型的なデータ・ウェアハウス環境では、すべての非一意列がビットマップ索引の候補です。
Bツリー索引は、カーディナリティが高いデータ(customer_name
やphone_number
など、固有な値を多く持つデータ)に対して最も効果的です。データ・ウェアハウスでは、Bツリー索引は、一意の列またはカーディナリティが非常に高い列(ほとんど一意である列)にのみ使用してください。データ・ウェアハウスの索引は、ほとんどがビットマップ索引であっても問題ありません。
非定型問合せなどを行う場合、ビットマップ索引によって問合せのパフォーマンスが大幅に向上します。結果のビットマップをROWIDに変換する前に、対応するブール操作をビットマップに対して直接実行することにより、問合せのWHERE
句で指定したAND
およびOR
条件は、すぐに解決されます。結果の行数が少ない場合は、全表スキャンを行うことなく、すぐに問合せの結果が戻されます。
例6-1 ビットマップ索引
次の例は、ある会社のcustomers
表の一部を示しています。
SELECT cust_id, cust_gender, cust_marital_status, cust_income_level FROM customers; CUST_ID C CUST_MARITAL_STATUS CUST_INCOME_LEVEL ---------- - -------------------- --------------------- ... 70 F D: 70,000 - 89,999 80 F married H: 150,000 - 169,999 90 M single H: 150,000 - 169,999 100 F I: 170,000 - 189,999 110 F married C: 50,000 - 69,999 120 M single F: 110,000 - 129,999 130 M J: 190,000 - 249,999 140 M married G: 130,000 - 149,999 ...
cust_gender
、cust_marital_status
およびcust_income_level
は、すべてカーディナリティが低い列(MARITAL_STATUSおよびREGIONは3つの値のみ、GENDERは2つの値のみ、INCOME_LEVELは12の値のみが存在する列)であるため、これらの列にはビットマップ索引が理想的です。cust_id
は一意の列であるため、この列にはビットマップ索引を作成しないでください。かわりに、この列に一意のBツリー索引を作成すると、最も効率的に表示および検索できます。
表6-1に、この例のcust_gender
列に対するビットマップ索引を示します。この索引は、2つの別々のビットマップで構成されており、それぞれが性別に対応しています。
表6-1 ビットマップ索引の例
gender='M' | gender='F' | |
---|---|---|
|
0 |
1 |
|
0 |
1 |
|
1 |
0 |
|
0 |
1 |
|
0 |
1 |
|
1 |
0 |
|
1 |
0 |
|
1 |
0 |
ビットマップの各エントリ(ビット)は、customers
表の1つの行に対応します。各ビットの値は、表にある対応する行の値に依存します。たとえば、ビットマップcust_gender='F'
には、最初のビットとして1が含まれています。これは、customers
表の最初の行にある性別がF
であるためです。ビットマップcust_gender='F'
の第3ビットは0です。これは、3行目の性別がF
でないためです。
会社の顧客の人口統計情報傾向を調査するアナリストが、「既婚者で、所得レベルがGまたはHの顧客がどれだけいるか」と質問するとします。この質問は、次の問合せで表すことができます。
SELECT COUNT(*) FROM customers WHERE cust_marital_status = 'married' AND cust_income_level IN ('H: 150,000 - 169,999', 'G: 130,000 - 149,999');
図6-1に示すように、ビットマップ索引は、単にビットマップにある1の数をカウントすることで、この問合せを効率的に処理できます。結果セットはビットマップまたはマージ操作を使用して検索され、ROWIDへの変換は不要です。さらに条件を満たす特定の顧客属性も確認するには、ビットマップからROWIDへの変換後に、結果ビットマップを使用して表にアクセスします。
ファクト表のみが問い合され、索引付けされた列に対する条件がある場合、またはファクト表が2つ以上のディメンション表と結合され、そのファクト表の外部キー列に対する索引があり、ディメンション表の列に対する条件がある場合に、ビットマップ索引は有効です。
次の条件が当てはまる場合、ファクト表の列はビットマップ索引の候補となります。
索引付けされた列の各個別値に対し行が100以上ある。この制限が当てはまる場合、ビットマップ索引は通常の索引よりも大幅に小さくなり、通常の索引よりも早く作成できます。たとえば、数十億の行がある表に、100万の個別値が含まれる場合などです。
また、次のいずれかの場合も該当します。
索引付けされた列が、問合せで制限される(WHERE
句で参照される)。
または
索引付けされた列が、ディメンション表の外部キーである。この場合、この索引によってスター型変換が行われる可能性が高くなります。
ビットマップ索引は、他のほとんどのタイプの索引とは異なり、NULL
値を持つ行を含みます。NULLの索引付けは、集計関数COUNT
が指定されている問合せなどの、いくつかのタイプのSQL文に有効です。
例6-2 ビットマップ索引
SELECT COUNT(*) FROM customers WHERE cust_marital_status IS NULL;
この問合せでは、cust_marital_status
のビットマップ索引が使用されます。この問合せでは、Bツリー索引を使用できないことに注意してください。Bツリー索引にはNULL
値は格納されないためです。
SELECT COUNT(*) FROM customers;
NULL
データを持つ行を含めて、表のすべての行に索引が付けられるため、すべてのビットマップ索引をこの問合せに使用できます。NULLに索引が付けられていない場合、オプティマイザは、NOT NULL
制約が指定されている列にある索引のみを使用できます。
ビットマップ索引をパーティション表に作成できますが、パーティション表に対してローカルである必要があり、グローバル索引にできません。パーティション表に作成できるグローバル索引は、パーティション化または非パーティション化のBツリー索引のみです。詳細は、『Oracle Database VLDBおよびパーティショニング・ガイド』を参照してください。
単一表のビットマップ索引の他に、ビットマップ結合索引を作成できます。これは、複数の表の結合に対するビットマップ索引です。ビットマップ結合索引では、索引付けされる表のビットマップは、結合先の表の値のために作成されます。データ・ウェアハウス環境では、結合条件はディメンション表の主キー列とファクト表の外部キー列の間の内部等価結合です。
ビットマップ結合索引を使用すると、パフォーマンスを大幅に向上させることができます。結合結果を格納することで、ビットマップ結合索引を使用するSQL文では、結合をまったく行わずに済みます。また、ビットマップ結合索引では、結合列に対する通常のビットマップ索引と比較して、個別値の数がさらに少なくなることがほとんどなので、ビットマップの圧縮率が高くなります。その結果、結合列に対する通常のビットマップ索引よりも、使用する領域が小さくなります。
ビットマップ結合索引では、事前に結合を行うもう1つの方法であるマテリアライズド・ビューよりも、格納効率がはるかに高くなります。これは、マテリアライズド・ビューでは、ファクト表のROWIDが圧縮されないためです。
ビットマップ結合索引は、スター・モデル環境で使用するのが最も一般的です。スター・モデル環境とは、大規模な表の索引列に、1つ以上の小規模な表が結合されている環境のことです。大規模な表のことをファクト表、小規模な表のことをディメンション表と呼びます。次の項では、ビットマップ結合索引でサポートされている4つの結合モデルについて説明します。スキーマのモデリング化技法については、第19章「スキーマのモデリング化技法」を参照してください。
例6-3 ビットマップ結合索引: 1つのディメンション表の列と1つのファクト表の結合
ビットマップ索引をcustomers
表のcust_gender
列に作成した「ビットマップ索引」の例とは異なり、ここでは、結合列customers(cust_gender)
のビットマップ結合索引をファクト表sales
に作成します。表sales
は、次のようにcust_id
値のみを格納します。
SELECT time_id, cust_id, amount_sold FROM sales; TIME_ID CUST_ID AMOUNT_SOLD --------- ---------- ----------- 01-JAN-98 29700 2291 01-JAN-98 3380 114 01-JAN-98 67830 553 01-JAN-98 179330 0 01-JAN-98 127520 195 01-JAN-98 33030 280 ...
このようなビットマップ結合索引を作成するには、列customers(cust_gender)
を表sales
に結合する必要があります。結合条件は、ビットマップ結合索引のCREATE
文の一部として指定します。次に例を示します。
CREATE BITMAP INDEX sales_cust_gender_bjix ON sales(customers.cust_gender) FROM sales, customers WHERE sales.cust_id = customers.cust_id LOCAL NOLOGGING COMPUTE STATISTICS;
次の問合せにより、ビットマップ結合索引に格納されるビットマップの作成に使用する結合結果が示されます。
SELECT sales.time_id, customers.cust_gender, sales.amount_sold FROM sales, customers WHERE sales.cust_id = customers.cust_id; TIME_ID C AMOUNT_SOLD --------- - ----------- 01-JAN-98 M 2291 01-JAN-98 F 114 01-JAN-98 M 553 01-JAN-98 M 0 01-JAN-98 M 195 01-JAN-98 M 280 01-JAN-98 M 32 ...
表6-2に、この例におけるビットマップ結合索引のビットマップ表現を示します。
表6-2 ビットマップ結合索引の例
cust_gender='M' | cust_gender='F' | |
---|---|---|
sales record 1 |
1 |
0 |
sales record 2 |
0 |
1 |
sales record 3 |
1 |
0 |
sales record 4 |
1 |
0 |
sales record 5 |
1 |
0 |
sales record 6 |
1 |
0 |
sales record 7 |
1 |
0 |
次の例のように、複数の列または複数の表を使用すると、他のビットマップ結合索引を作成できます。
例6-4 ビットマップ結合索引: 複数のディメンション列と1つのファクト表の結合
ビットマップ結合索引は、次の例のように、1つのディメンション表の複数の列に作成できます。次の例では、sh
スキーマのcustomers(cust_gender, cust_marital_status)
を使用しています。
CREATE BITMAP INDEX sales_cust_gender_ms_bjix ON sales(customers.cust_gender, customers.cust_marital_status) FROM sales, customers WHERE sales.cust_id = customers.cust_id LOCAL NOLOGGING COMPUTE STATISTICS;
例6-5 ビットマップ結合索引: 複数のディメンション表と1つのファクト表の結合
ビットマップ結合索引は、次の例のように、複数のディメンション表に作成できます。次の例では、customers(cust_gender)
とproducts(prod_category)
を使用しています。
CREATE BITMAP INDEX sales_c_gender_p_cat_bjix ON sales(customers.cust_gender, products.prod_category) FROM sales, customers, products WHERE sales.cust_id = customers.cust_id AND sales.prod_id = products.prod_id LOCAL NOLOGGING COMPUTE STATISTICS;
例6-6 ビットマップ結合索引: スノーフレーク・スキーマ
複数の表のビットマップ結合索引を作成できます。この場合、索引付けされた列は、別の表を使用して索引付けされた表に結合されます。たとえば、countries
表がsales
表と直接結合されていなくても、countries.country_name
の索引を作成できます。かわりに、countries
表がcustomers
表に結合され、customers
表はsales
表に結合されます。このタイプのスキーマは、一般にスノーフレーク・スキーマと呼ばれます。
CREATE BITMAP INDEX sales_co_country_name_bjix ON sales(countries.country_name) FROM sales, customers, countries WHERE sales.cust_id = customers.cust_id AND customers.country_id = countries.country_id LOCAL NOLOGGING COMPUTE STATISTICS;
結合結果を格納する必要があるため、ビットマップ結合索引には次の制限事項があります。
パラレルDMLはファクト表でのみサポートされます。ディメンション表に対してパラレルDMLを行うと、索引はUNUSABLEのマークが付けられます。
ビットマップ結合索引を使用する場合、異なるトランザクションで同時に更新できる表は1つのみです。
結合に2回同じ表は使用できません。
一時表には、ビットマップ結合索引を作成できません。
索引の列は、すべてディメンション表の列である必要があります。
ディメンション表の結合列は、主キー列であるか、一意制約を持つ必要があります。
ファクト表との結合に関与しているディメンション表の1つ以上の列は、主キー列であるか、一意制約を持つ必要があります。
ディメンション表に複合主キーがある場合、主キーの各列が結合の一部である必要があります。
通常のビットマップ索引の作成に関する制限事項も、ビットマップ結合索引に適用されます。たとえば、UNIQUE
属性を持つビットマップ索引は作成できません。その他の制限事項については、『Oracle Database SQL言語リファレンス』を参照してください。
Bツリー索引は、木をさかさまにしたような構造になっています。索引の最下位層レベルには、実際のデータ値および対応する行へのポインタがあります。これは、本の索引に、各索引エントリに対応するページ数があることとよく似ています。
一般に、典型的な問合せが索引付けされた列を参照して少数の行を取り出すことがわかっている場合に、Bツリー索引を使用します。これらの問合せでは、索引を参照する方が、行を迅速に検索できます。しかし、本の索引にたとえると、本のトピックを1つずつ参照する場合、そのトピックを索引で調べてから該当ページを検索することはありません。本のすべての章を読む方が速いことになります。これと同様に、表内のほとんどの行を取り出す場合、索引を参照して表の行を検索するのでは意味がありません。かわりに、表を読み込むかまたはスキャンします。
データ・ウェアハウスでは、一意キーの索引付けに最もよく使用されるのはBツリー索引です。多くの場合、データ・ウェアハウスにあるこれらの列に索引付けを行う必要はありません。これは、事前に実行されるETL処理の中で一意性はすでに確保されており、通常のデータ・ウェアハウスの問合せは、こうした索引を使用してもパフォーマンスが向上しないためです。Bツリー索引は、第3正規形のスキーマを使用する環境で使用するのが一般的です。ほとんどのデータ・ウェアハウス環境では、ビットマップ索引が、Bツリー索引よりもよく使用されます。
ビットマップ索引は常に、ユーザーの操作を必要とすることなく、独自の方法で圧縮されて格納されます。Bツリー索引は、明示的に圧縮して格納することで、領域を大幅に節約することが可能です。この際、各索引ブロックにはより多くのキーが格納され、I/Oの削減およびパフォーマンスの向上にもつながります。
キーの圧縮によって、Bツリー索引を圧縮できます。この結果、重複値による領域のオーバーヘッドが低減されます。非一意索引では、すべての索引列を圧縮形式で格納できますが、一意索引では、少なくとも1つの索引列は圧縮せずに格納する必要があります。
通常、索引のキーは、グループ化要素と一意要素という2つの要素を持っています。一意要素を持つようにキーが定義されていない場合は、ROWIDをグループ化要素に追加する形で一意要素が提供されます。キーの圧縮では、グループ化要素が分離され、複数の一意要素が共有できるような形で格納されます。圧縮対象として選択された列のカーディナリティによって、実現可能な圧縮率が決まります。したがって、たとえば5つの列からなる一意索引の一意性のほとんどが、後ろの2列によって実現されている場合は、先頭の3列を圧縮して格納するのが最も効率的ということになります。4つの列を圧縮する場合は、重複性はほとんど失われ、圧縮率が低下します。
キーの圧縮によって、索引の領域要件は低くなりますが、索引スキャン時のキー列値の再構築に必要なCPUタイムが長くなる場合があります。また、接頭辞エントリごとに4バイトのオーバーヘッドが生じるので、追加の領域オーバーヘッドも発生します。
パーティション表のBツリー索引は、ローカルまたはグローバルにできます。Oracle8i以前のリリースでは、データ・ウェアハウス環境ではグローバル索引を使用しないようにお薦めしていました。これは、パーティションのDDL文(ALTER
TABLE
... DROP
PARTITION
など)により索引全体が無効になり、索引の再構築に手間がかかったためです。Oracle Database 10gでは、グローバル索引をメンテナンスしても、DDL後にUNUSABLEとマークされることはありません。この拡張により、データ・ウェアハウス環境におけるグローバル索引の効率が向上しています。
ただし、ローカル索引の方がグローバル索引より一般的です。グローバル索引を使用する必要があるのは、ローカル索引では満たせない特定の要件(非パーティション化キーの一意索引や、パフォーマンス要件など)がある場合です。
パーティション表のビットマップ索引は、常にローカルです。