Oracle INDEXを作成したときのパフォーマンスへの効果を探る

 2017.02.22  株式会社システムインテグレータ

これまで、パフォーマンスチューニングに関する記事を公開してきました。

今回は、

「インデックスの作成はパフォーマンス改善に役立つ」

という定説について、結合クエリを例にデータを取得して再確認していきます。初心を忘れないようインデックスの作成前後の効果を素直にみていこうかと思います。

Oracle INDEXを作成したときのパフォーマンスへの効果を探る 1

 

パフォーマンスチューニングに関する技術については専門書にお任せし、今回はサンプルのデータで実験を行った結果について記載します。

検証対象のテーブル

今回使用するテーブルは以下のようなテーブルです。比較的シンプルなものにします。

・100万件の受注テーブル

・500万件の受注明細テーブル(5明細/受注)

ER図は以下の通り。SI Object Browser ERを使って作成しました。

Oracle INDEXを作成したときのパフォーマンスへの効果を探る 2

実際のDDLは英語にします。下記になります。
Oracle INDEXを作成したときのパフォーマンスへの効果を探る 3
 

 DDLはこちら。SI Object Browser ERはER図を作成すると、DDLが自動生成されるので便利です。

CREATE TABLE DBLAB_ORDER_TBL
(
    ORDER_NO                        NVARCHAR2(10) NOT NULL,
    ORDER_DATE                      DATE NOT NULL,
    STORE_ID                        NUMBER(8,0) NOT NULL,
    ORDER_AMNT                      NUMBER(10,0) NOT NULL,
    MEMBER_ID                       NVARCHAR2(10) NOT NULL,
    CMP_TAX                         NUMBER(10,0) NOT NULL,
    FLAG_01                         NUMBER(1,0),
    FLAG_02                         NUMBER(1,0),
    SLIP_COMMENT                    NVARCHAR2(1000),
    UPDATE_DATE                     DATE NOT NULL,
    UPDATER                         NVARCHAR2(12)
)
/
ALTER TABLE DBLAB_ORDER_TBL
ADD(CONSTRAINT PK_DBLAB_ORDER_TBL PRIMARY KEY (ORDER_NO) USING INDEX)
/
--受注明細テーブル
CREATE TABLE DBLAB_ORDER_DETAIL_TBL
(
    ORDER_NO                        NVARCHAR2(10) NOT NULL,
    SO_ROW_NO                       NUMBER(3,0) NOT NULL,
    PROD_CODE                       NVARCHAR2(16) NOT NULL,
    UNITPRICE                       NUMBER(8,0) NOT NULL,
    QUANTITY                        NUMBER(4,0) NOT NULL,
    CMP_TAX_RATE                    NUMBER(3,0),
    DISCOUNT                        NUMBER(4,0) NOT NULL,
    DELIVERY_DATE                   DATE,
    UPDATE_DATE                     DATE NOT NULL,
    UPDATER                         NVARCHAR2(12)
)
/
ALTER TABLE DBLAB_ORDER_DETAIL_TBL
ADD(CONSTRAINT PK_DBLAB_ORDER_DETAIL_TBL PRIMARY KEY (ORDER_NO, SO_ROW_NO) USING INDEX)
/

検証するクエリ

下記のようなものとします。

(1)二つを結合して100万件の受注額(明細の集計)を取得する。

SELECT A.ORDER_NO
      ,SUM(B.UNITPRICE * B.QUANTITY - B.DISCOUNT) 販売額
      ,SUM(B.QUANTITY) 販売数
FROM   DBLAB_ORDER_TBL A
      ,DBLAB_ORDER_DETAIL_TBL B
WHERE  A.ORDER_NO = B.ORDER_NO
GROUP BY A.ORDER_NO
ORDER BY A.ORDER_NO;

(2)二つを結合して100万件の受注額(明細の集計)を取得する。
受注のフラグ1が1のものを対象とする。(実は全件となります)

SELECT A.ORDER_NO
      ,SUM(B.UNITPRICE * B.QUANTITY - B.DISCOUNT) 販売額
      ,SUM(B.QUANTITY) 販売数
FROM   DBLAB_ORDER_TBL A
      ,DBLAB_ORDER_DETAIL_TBL B
WHERE  A.ORDER_NO = B.ORDER_NO
AND    A.FLAG_01 = 1    -- この条件が増えている
GROUP BY A.ORDER_NO
ORDER BY A.ORDER_NO ;

二つのクエリの違いは、WHERE句のFLAG_01の有無です。処理する件数や集計方法はそのままとしています。

集計結果が同一件数となるこの二つのクエリについて、インデックスを作成する前後での効果の度合いを見たいと思います。 

検証する条件

以下の条件で検証します。

・追加インデックスなし

・インデックス追加(受注明細)

・インデックス追加(受注)

追加するインデックスは下記の通りです。SI Object Browserでインデックスを作成し、自動生成されたDDLです。

受注明細のインデックス

集計対象となる金額や個数にインデックスを付与します。

CREATE INDEX DBLAB_IDX_TEST
ON DBLAB_ORDER_DETAIL_TBL
(ORDER_NO, UNITPRICE, QUANTITY, DISCOUNT)
PCTFREE 10
INITRANS 2
MAXTRANS 255
TABLESPACE USERS
STORAGE(INITIAL 64K NEXT 1M MINEXTENTS 1 MAXEXTENTS 2147483645 BUFFER_POOL DEFAULT)
LOGGING
VISIBLE

受注のインデックス

WHERE句の対象となるPKとフラグにインデックスを付与します。

CREATE INDEX DBLAB_IDX_ORDER_FLG1
ON DBLAB_ORDER_TBL
(ORDER_NO, FLAG_01)
PCTFREE 10
INITRANS 2
MAXTRANS 255
TABLESPACE USERS
STORAGE(INITIAL 64K NEXT 1M MINEXTENTS 1 MAXEXTENTS 2147483645 BUFFER_POOL DEFAULT)
LOGGING
VISIBLE

検証方法

上記の条件ごとに5回クエリを発行した実行時間の平均値で検証します。1回目の実行は除き、キャッシュ情報を消さないで実行した2~6回目の結果を対象とします。

実行時間の値は、SI Object Browser のSQL画面ステータスバーに表示される値を使用します。 

検証結果

結果は以下の通りとなりました。

(1)二つを結合して100万件の受注額(明細の集計)を取得する

Oracle INDEXを作成したときのパフォーマンスへの効果を探る 4

集計処理の対象となる項目にインデックスを付与することで効果が出る、が確認できました。

定説通りですが、100万件の結果に対して8割近くの効果があるんですね。

 

(2)二つを結合して100万件の受注額(明細の集計)を取得する。
受注のフラグ1が1のものを対象とする。(実は全件となります)

Oracle INDEXを作成したときのパフォーマンスへの効果を探る 5

まず、WHERE句が一つ追加になるだけで、追加インデックスなしの実行時間が2.5倍弱になりました。全件検索が入ったためであり、これは件数に依存して増加するものでしょう。

集計処理の対象となる項目にインデックスを付与しても、大きな効果はありませんでした。

しかしながら、WHERE句のフラグに対するインデックスを付与したとたん、パフォーマンスは一気に改善されました。これも定説通りといえます。

WHERE句が増えただけで、インデックスの無い状態でのパフォーマンスには大きな隔たりがありましたが、インデックスを付与することでほぼ同じくらいの速度となりました。 [RELATED_POSTS]

追加検証(WHERE句を変えたらどうなる)

では、このパフォーマンスが上がったクエリに対して仕様変更が発生した場合にどうなるかをやってみます。例として、WHERE条件が追加になった場合、で検証してみます。

(3)二つを結合して100万件の受注額(明細の集計)を取得する。
受注のフラグ1が1のものを対象とする。さらに受注のフラグ2が2のものを対象とする。(実は全件となります)

SELECT A.ORDER_NO
      ,SUM(B.UNITPRICE * B.QUANTITY - B.DISCOUNT) 販売額
      ,SUM(B.QUANTITY) 販売数
FROM   DBLAB_ORDER_TBL A
      ,DBLAB_ORDER_DETAIL_TBL B
WHERE  A.ORDER_NO = B.ORDER_NO
AND    A.FLAG_01 = 1
AND    A.FLAG_02 = 2    -- 仕様変更でこの条件を追加!
GROUP BY A.ORDER_NO
ORDER BY A.ORDER_NO ;

このクエリのパフォーマンスは、下記となりました。

Oracle INDEXを作成したときのパフォーマンスへの効果を探る 6

フラグ1に付与したインデックスの効果がなくなってしまいました。

フラグ2を追加したことにより、全件アクセスが発生したことに起因しています。これは実行計画からも確認できます。以下は、SI Object Browser のSQL画面から確認した実行計画の内容です。WHERE句を修正するということは、現在のパフォーマンスに影響を与えることがある。という意識を忘れてはいけないようです。

フラグ2追加前の実行計画

Oracle INDEXを作成したときのパフォーマンスへの効果を探る 7フラグ2追加後の実行計画

Oracle INDEXを作成したときのパフォーマンスへの効果を探る 8

追加検証(インデックスを変えたらどうなる)

さらにインデックスを変更した場合のパフォーマンスを見てみます。以下の2パターンのデータを測定した結果をグラフに追加します。

・フラグ2だけのインデックス

CREATE INDEX DBLAB_IDX_ORDER_FLG2_ONLY
ON DBLAB_ORDER_TBL
(FLAG_02)
PCTFREE 10
INITRANS 2
MAXTRANS 255
TABLESPACE USERS
STORAGE(INITIAL 64K NEXT 1M MINEXTENTS 1 MAXEXTENTS 2147483645 BUFFER_POOL DEFAULT)
LOGGING
VISIBLE

・受注番号、フラグ1、フラグ2の複合インデックス(インデックス再作成)

CREATE INDEX DBLAB_IDX_ORDER_FLG1_FLG2
ON DBLAB_ORDER_TBL
(ORDER_NO, FLAG_01, FLG_2)
PCTFREE 10
INITRANS 2
MAXTRANS 255
TABLESPACE USERS
STORAGE(INITIAL 64K NEXT 1M MINEXTENTS 1 MAXEXTENTS 2147483645 BUFFER_POOL DEFAULT)
LOGGING
VISIBLE

上記の結果をプロットした結果が、以下となりました。

Oracle INDEXを作成したときのパフォーマンスへの効果を探る 9

インデックスの追加によりパフォーマンスの改善は確認できました。

一方で、フラグ2のみのインデックスを追加と複合インデックスに変えたとでは、複合インデックスが若干パフォーマンスが勝る結果となりました。

フラグ2のみのインデックスを追加したときの実行計画は以下です。インデックスはきちんと使われていますね。

Oracle INDEXを作成したときのパフォーマンスへの効果を探る 10複合インデックスにした場合の実行計画は以下です。コストが抑えられる結果となります。

Oracle INDEXを作成したときのパフォーマンスへの効果を探る 11

結論

「インデックスの作成はパフォーマンス改善に役立つ」という定説は、当たり前のことではありますが、効果があるケースとないケースを再認識できました。

パフォーマンスチューニングを施したSQLに、不具合対応でWHERE情報を変更する必要が発生し、結果としてパフォーマンスが元に戻る。なんていう事象も発生しうることも見えました。

インデックスを追加すればSELECTのクエリには効果がある。でも、INSERTとUPDATEのパフォーマンスは下がる。

対象のテーブルについてどのパフォーマンスを求めるのか。インデックスを作成する場合は、対象テーブルで処理されるクエリの件数、CRUDの頻度などを鑑みてインデックスを作成する必要がありそうです。定説は定説ですが、あくまで基本、利用者次第、バランスを見て検討ください。

次は、データ件数をもう少し多く、条件をもう少し複雑、にして再度検証してみようかなと考えています。


RELATED POST関連記事


RECENT POST「DBlab」の最新記事


DBlab

Oracle デッドロックの原因を追いかけてみる

DBlab

Oracle パーティション検証完結編

DBlab

Oracle B-treeインデックスとビットマップインデックス

DBlab

SQLServer テンポラルテーブルを検証してみた

Oracle INDEXを作成したときのパフォーマンスへの効果を探る
新規CTA