3

こんにちは :) 私は、Oracle 10g データベースでボリューム サンプリングを行うためのツールを構築しています。クエリは次のとおりです。

SELECT count(*) 
FROM product
JOIN customer ON product.CUSTOMER_ID = customer.ID
WHERE 
 (    product.CATEGORY = 'some first category criteria'
  AND customer.REGION = 'some first region criteria'
  AND ...)
 OR
 (    product.CATEGORY = 'some second category criteria'
  AND customer.REGION = 'some second region criteria'
  AND ...)
 OR ...

このクエリで必要なのは、カウントを行うことだけです。問題は、ボリュームが大きいことです。各テーブルに約 3,000 万行あり、このクエリを応答性の高いものにしたいと考えています。

これまでのところ、複合インデックスをオンcustomer (<search criteria column>, CUSTOMER_ID)にすることは非常に役に立ちました。JOINインデックス付きフィルター操作の後にOracleがヘルパーになると思います。

(... AND ... AND ...)ブロックには、約 50,000 行が含まれると予想されます。検索条件で使用されるすべての列には、約 1000 個の値のセットの値があります。

count(*)特にOracleには組み込みのOLAPモジュール(およびCUBE操作?)があるため、sのみを実行するという事実に関して、どのようなアプローチを実装できるか疑問に思っていました。また、よく考えられたインデックスとヒントによって、物事は大幅に改善されると確信しています。

これをどのように設計しますか?

4

1 に答える 1

1

これは、ビットマップ インデックスの適切な候補のように見えます。

ビットマップ インデックスは主に、データ ウェアハウスやクエリがアドホックな方法で多数の列を参照する環境向けに設計されています。ビットマップ インデックスが必要になる状況には、次のようなものがあります。

インデックス付きの列のカーディナリティは低くなります。つまり、個別の値の数がテーブルの行の数に比べて少なくなります。

インデックス付きテーブルは読み取り専用であるか、DML ステートメントによる大幅な変更の対象ではありません。

具体的には、ここではビットマップ結合インデックスが理想的です。マニュアルの例は、データ モデルとも一致します。以下のモデルとデータを再作成しようとしましたが、ビットマップ結合インデックスは、他のソリューションよりも数桁速く実行されるようです。

サンプルデータ

--Create tables
create table customer
(
    customer_id number,
    region      varchar2(100) not null
) nologging;

create table product
(
    product_id  number,
    customer_id number not null,
    category    varchar2(100) not null
) nologging;


--Load 30M rows, 1M rows at a time.  Takes about 6 minutes.
begin
    for i in 1 .. 30 loop
        insert /*+ append */ into customer
        select (1000000*i)+level, 'Region '||trunc(dbms_random.value(1, 1000))
        from dual connect by level <= 1000000;
        commit;

        insert /*+ append */ into product
        select (1000000*i)+level, (1000000*i)+level
            ,'Category '||trunc(dbms_random.value(1, 1000))
        from dual connect by level <= 1000000;
        commit;
    end loop;
end;
/

--Add primary keys and foreign key constraints.
alter table customer add constraint customer_pk primary key (customer_id);
alter table product add constraint product_pk primary key (product_id);
alter table product add constraint product_customer_fk
    foreign key (customer_id) references customer(customer_id);

--Gather stats
begin
    dbms_stats.gather_table_stats(user, 'CUSTOMER');
    dbms_stats.gather_table_stats(user, 'PRODUCT');
end;
/

インデックスなし - 遅い

さすがに性能はイマイチ。このサンプル クエリは、私のマシンでは約 75 秒かかります。

SELECT count(*) 
FROM product
JOIN customer ON product.CUSTOMER_ID = customer.customer_id
WHERE (product.CATEGORY = 'Category 1' AND customer.REGION = 'Region 1')
 OR   (product.CATEGORY = 'Category 2' AND customer.REGION = 'Region 2')
 OR   (product.CATEGORY = 'Category 888' AND customer.REGION = 'Region 888');

B ツリー インデックス - まだ遅い

計画は変更されますが、パフォーマンスは同じままです。これは、私の例が最悪の場合のインデックス作成シナリオであり、データが完全にランダムであるためであると考えられます。

create index customer_idx on customer(region);
create index product_idx on product(category);

begin
    dbms_stats.gather_table_stats(user, 'CUSTOMER');
    dbms_stats.gather_table_stats(user, 'PRODUCT');
end;
/

ビットマップ インデックス - 少し改善

これにより、パフォーマンスがわずかに向上し、約 61 秒になります。

drop index customer_idx;
drop index product_idx;

create bitmap index customer_bidx on customer(region);
create bitmap index product_bidx on product(category);

begin
    dbms_stats.gather_table_stats(user, 'CUSTOMER');
    dbms_stats.gather_table_stats(user, 'PRODUCT');
end;
/

ビットマップ結合インデックス - 信じられないほど高速

これで、クエリはほぼ瞬時に結果を返します。私の IDE はそれを 0 秒としてカウントします。

drop index customer_idx;
drop index product_idx;

create bitmap index customer_product_bjix
on product(product.category, customer.region)
FROM product, customer
where product.CUSTOMER_ID = customer.customer_id;

begin
    dbms_stats.gather_table_stats(user, 'CUSTOMER');
    dbms_stats.gather_table_stats(user, 'PRODUCT');
end;
/

インデックス コスト

ビットマップ結合インデックスは、B ツリーまたはビットマップ インデックスよりも作成に時間がかかります。B ツリー インデックスは、ビットマップまたはビットマップ結合インデックスに比べて非常に大きくなります。

select segment_name, bytes/1024/1024 MB
from dba_segments
where segment_name in ('CUSTOMER_IDX', 'PRODUCT_IDX'
    ,'CUSTOMER_BIDX', 'PRODUCT_BIDX',  'CUSTOMER_PRODUCT_BJIX');


SEGMENT_NAME            MB
------------            --
CUSTOMER_IDX            726
PRODUCT_IDX             792
CUSTOMER_BIDX            88
PRODUCT_BIDX             96
CUSTOMER_PRODUCT_BJIX   184

クエリ スタイル

これはパフォーマンスには影響しませんが、次のようにクエリを縮小できる場合があります。

SELECT count(*) 
FROM product
JOIN customer ON product.CUSTOMER_ID = customer.customer_id
WHERE (product.category, customer.region)
    in (('Category 1', 'Region 1'),
        ('Category 2', 'Region 2'),
        ('Category 888', 'Region 888'));
于 2013-06-07T04:51:36.567 に答える